roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     }
1019     
1020 });
1021
1022
1023  
1024 /*
1025  * Based on:
1026  * Ext JS Library 1.1.1
1027  * Copyright(c) 2006-2007, Ext JS, LLC.
1028  *
1029  * Originally Released Under LGPL - original licence link has changed is not relivant.
1030  *
1031  * Fork - LGPL
1032  * <script type="text/javascript">
1033  */
1034
1035 /**
1036  * @class Date
1037  *
1038  * The date parsing and format syntax is a subset of
1039  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1040  * supported will provide results equivalent to their PHP versions.
1041  *
1042  * Following is the list of all currently supported formats:
1043  *<pre>
1044 Sample date:
1045 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1046
1047 Format  Output      Description
1048 ------  ----------  --------------------------------------------------------------
1049   d      10         Day of the month, 2 digits with leading zeros
1050   D      Wed        A textual representation of a day, three letters
1051   j      10         Day of the month without leading zeros
1052   l      Wednesday  A full textual representation of the day of the week
1053   S      th         English ordinal day of month suffix, 2 chars (use with j)
1054   w      3          Numeric representation of the day of the week
1055   z      9          The julian date, or day of the year (0-365)
1056   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1057   F      January    A full textual representation of the month
1058   m      01         Numeric representation of a month, with leading zeros
1059   M      Jan        Month name abbreviation, three letters
1060   n      1          Numeric representation of a month, without leading zeros
1061   t      31         Number of days in the given month
1062   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1063   Y      2007       A full numeric representation of a year, 4 digits
1064   y      07         A two digit representation of a year
1065   a      pm         Lowercase Ante meridiem and Post meridiem
1066   A      PM         Uppercase Ante meridiem and Post meridiem
1067   g      3          12-hour format of an hour without leading zeros
1068   G      15         24-hour format of an hour without leading zeros
1069   h      03         12-hour format of an hour with leading zeros
1070   H      15         24-hour format of an hour with leading zeros
1071   i      05         Minutes with leading zeros
1072   s      01         Seconds, with leading zeros
1073   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1074   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1075   T      CST        Timezone setting of the machine running the code
1076   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1077 </pre>
1078  *
1079  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1080  * <pre><code>
1081 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1082 document.write(dt.format('Y-m-d'));                         //2007-01-10
1083 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1084 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1085  </code></pre>
1086  *
1087  * Here are some standard date/time patterns that you might find helpful.  They
1088  * are not part of the source of Date.js, but to use them you can simply copy this
1089  * block of code into any script that is included after Date.js and they will also become
1090  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1091  * <pre><code>
1092 Date.patterns = {
1093     ISO8601Long:"Y-m-d H:i:s",
1094     ISO8601Short:"Y-m-d",
1095     ShortDate: "n/j/Y",
1096     LongDate: "l, F d, Y",
1097     FullDateTime: "l, F d, Y g:i:s A",
1098     MonthDay: "F d",
1099     ShortTime: "g:i A",
1100     LongTime: "g:i:s A",
1101     SortableDateTime: "Y-m-d\\TH:i:s",
1102     UniversalSortableDateTime: "Y-m-d H:i:sO",
1103     YearMonth: "F, Y"
1104 };
1105 </code></pre>
1106  *
1107  * Example usage:
1108  * <pre><code>
1109 var dt = new Date();
1110 document.write(dt.format(Date.patterns.ShortDate));
1111  </code></pre>
1112  */
1113
1114 /*
1115  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1116  * They generate precompiled functions from date formats instead of parsing and
1117  * processing the pattern every time you format a date.  These functions are available
1118  * on every Date object (any javascript function).
1119  *
1120  * The original article and download are here:
1121  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1122  *
1123  */
1124  
1125  
1126  // was in core
1127 /**
1128  Returns the number of milliseconds between this date and date
1129  @param {Date} date (optional) Defaults to now
1130  @return {Number} The diff in milliseconds
1131  @member Date getElapsed
1132  */
1133 Date.prototype.getElapsed = function(date) {
1134         return Math.abs((date || new Date()).getTime()-this.getTime());
1135 };
1136 // was in date file..
1137
1138
1139 // private
1140 Date.parseFunctions = {count:0};
1141 // private
1142 Date.parseRegexes = [];
1143 // private
1144 Date.formatFunctions = {count:0};
1145
1146 // private
1147 Date.prototype.dateFormat = function(format) {
1148     if (Date.formatFunctions[format] == null) {
1149         Date.createNewFormat(format);
1150     }
1151     var func = Date.formatFunctions[format];
1152     return this[func]();
1153 };
1154
1155
1156 /**
1157  * Formats a date given the supplied format string
1158  * @param {String} format The format string
1159  * @return {String} The formatted date
1160  * @method
1161  */
1162 Date.prototype.format = Date.prototype.dateFormat;
1163
1164 // private
1165 Date.createNewFormat = function(format) {
1166     var funcName = "format" + Date.formatFunctions.count++;
1167     Date.formatFunctions[format] = funcName;
1168     var code = "Date.prototype." + funcName + " = function(){return ";
1169     var special = false;
1170     var ch = '';
1171     for (var i = 0; i < format.length; ++i) {
1172         ch = format.charAt(i);
1173         if (!special && ch == "\\") {
1174             special = true;
1175         }
1176         else if (special) {
1177             special = false;
1178             code += "'" + String.escape(ch) + "' + ";
1179         }
1180         else {
1181             code += Date.getFormatCode(ch);
1182         }
1183     }
1184     /** eval:var:zzzzzzzzzzzzz */
1185     eval(code.substring(0, code.length - 3) + ";}");
1186 };
1187
1188 // private
1189 Date.getFormatCode = function(character) {
1190     switch (character) {
1191     case "d":
1192         return "String.leftPad(this.getDate(), 2, '0') + ";
1193     case "D":
1194         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1195     case "j":
1196         return "this.getDate() + ";
1197     case "l":
1198         return "Date.dayNames[this.getDay()] + ";
1199     case "S":
1200         return "this.getSuffix() + ";
1201     case "w":
1202         return "this.getDay() + ";
1203     case "z":
1204         return "this.getDayOfYear() + ";
1205     case "W":
1206         return "this.getWeekOfYear() + ";
1207     case "F":
1208         return "Date.monthNames[this.getMonth()] + ";
1209     case "m":
1210         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1211     case "M":
1212         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1213     case "n":
1214         return "(this.getMonth() + 1) + ";
1215     case "t":
1216         return "this.getDaysInMonth() + ";
1217     case "L":
1218         return "(this.isLeapYear() ? 1 : 0) + ";
1219     case "Y":
1220         return "this.getFullYear() + ";
1221     case "y":
1222         return "('' + this.getFullYear()).substring(2, 4) + ";
1223     case "a":
1224         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1225     case "A":
1226         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1227     case "g":
1228         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1229     case "G":
1230         return "this.getHours() + ";
1231     case "h":
1232         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1233     case "H":
1234         return "String.leftPad(this.getHours(), 2, '0') + ";
1235     case "i":
1236         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1237     case "s":
1238         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1239     case "O":
1240         return "this.getGMTOffset() + ";
1241     case "P":
1242         return "this.getGMTColonOffset() + ";
1243     case "T":
1244         return "this.getTimezone() + ";
1245     case "Z":
1246         return "(this.getTimezoneOffset() * -60) + ";
1247     default:
1248         return "'" + String.escape(character) + "' + ";
1249     }
1250 };
1251
1252 /**
1253  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1254  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1255  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1256  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1257  * string or the parse operation will fail.
1258  * Example Usage:
1259 <pre><code>
1260 //dt = Fri May 25 2007 (current date)
1261 var dt = new Date();
1262
1263 //dt = Thu May 25 2006 (today's month/day in 2006)
1264 dt = Date.parseDate("2006", "Y");
1265
1266 //dt = Sun Jan 15 2006 (all date parts specified)
1267 dt = Date.parseDate("2006-1-15", "Y-m-d");
1268
1269 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1270 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1271 </code></pre>
1272  * @param {String} input The unparsed date as a string
1273  * @param {String} format The format the date is in
1274  * @return {Date} The parsed date
1275  * @static
1276  */
1277 Date.parseDate = function(input, format) {
1278     if (Date.parseFunctions[format] == null) {
1279         Date.createParser(format);
1280     }
1281     var func = Date.parseFunctions[format];
1282     return Date[func](input);
1283 };
1284 /**
1285  * @private
1286  */
1287
1288 Date.createParser = function(format) {
1289     var funcName = "parse" + Date.parseFunctions.count++;
1290     var regexNum = Date.parseRegexes.length;
1291     var currentGroup = 1;
1292     Date.parseFunctions[format] = funcName;
1293
1294     var code = "Date." + funcName + " = function(input){\n"
1295         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1296         + "var d = new Date();\n"
1297         + "y = d.getFullYear();\n"
1298         + "m = d.getMonth();\n"
1299         + "d = d.getDate();\n"
1300         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1301         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1302         + "if (results && results.length > 0) {";
1303     var regex = "";
1304
1305     var special = false;
1306     var ch = '';
1307     for (var i = 0; i < format.length; ++i) {
1308         ch = format.charAt(i);
1309         if (!special && ch == "\\") {
1310             special = true;
1311         }
1312         else if (special) {
1313             special = false;
1314             regex += String.escape(ch);
1315         }
1316         else {
1317             var obj = Date.formatCodeToRegex(ch, currentGroup);
1318             currentGroup += obj.g;
1319             regex += obj.s;
1320             if (obj.g && obj.c) {
1321                 code += obj.c;
1322             }
1323         }
1324     }
1325
1326     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1327         + "{v = new Date(y, m, d, h, i, s);}\n"
1328         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1329         + "{v = new Date(y, m, d, h, i);}\n"
1330         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1331         + "{v = new Date(y, m, d, h);}\n"
1332         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1333         + "{v = new Date(y, m, d);}\n"
1334         + "else if (y >= 0 && m >= 0)\n"
1335         + "{v = new Date(y, m);}\n"
1336         + "else if (y >= 0)\n"
1337         + "{v = new Date(y);}\n"
1338         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1339         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1340         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1341         + ";}";
1342
1343     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1344     /** eval:var:zzzzzzzzzzzzz */
1345     eval(code);
1346 };
1347
1348 // private
1349 Date.formatCodeToRegex = function(character, currentGroup) {
1350     switch (character) {
1351     case "D":
1352         return {g:0,
1353         c:null,
1354         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1355     case "j":
1356         return {g:1,
1357             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1358             s:"(\\d{1,2})"}; // day of month without leading zeroes
1359     case "d":
1360         return {g:1,
1361             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1362             s:"(\\d{2})"}; // day of month with leading zeroes
1363     case "l":
1364         return {g:0,
1365             c:null,
1366             s:"(?:" + Date.dayNames.join("|") + ")"};
1367     case "S":
1368         return {g:0,
1369             c:null,
1370             s:"(?:st|nd|rd|th)"};
1371     case "w":
1372         return {g:0,
1373             c:null,
1374             s:"\\d"};
1375     case "z":
1376         return {g:0,
1377             c:null,
1378             s:"(?:\\d{1,3})"};
1379     case "W":
1380         return {g:0,
1381             c:null,
1382             s:"(?:\\d{2})"};
1383     case "F":
1384         return {g:1,
1385             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1386             s:"(" + Date.monthNames.join("|") + ")"};
1387     case "M":
1388         return {g:1,
1389             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1390             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1391     case "n":
1392         return {g:1,
1393             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1394             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1395     case "m":
1396         return {g:1,
1397             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1398             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1399     case "t":
1400         return {g:0,
1401             c:null,
1402             s:"\\d{1,2}"};
1403     case "L":
1404         return {g:0,
1405             c:null,
1406             s:"(?:1|0)"};
1407     case "Y":
1408         return {g:1,
1409             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1410             s:"(\\d{4})"};
1411     case "y":
1412         return {g:1,
1413             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1414                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1415             s:"(\\d{1,2})"};
1416     case "a":
1417         return {g:1,
1418             c:"if (results[" + currentGroup + "] == 'am') {\n"
1419                 + "if (h == 12) { h = 0; }\n"
1420                 + "} else { if (h < 12) { h += 12; }}",
1421             s:"(am|pm)"};
1422     case "A":
1423         return {g:1,
1424             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1425                 + "if (h == 12) { h = 0; }\n"
1426                 + "} else { if (h < 12) { h += 12; }}",
1427             s:"(AM|PM)"};
1428     case "g":
1429     case "G":
1430         return {g:1,
1431             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1432             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1433     case "h":
1434     case "H":
1435         return {g:1,
1436             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1437             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1438     case "i":
1439         return {g:1,
1440             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1441             s:"(\\d{2})"};
1442     case "s":
1443         return {g:1,
1444             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1445             s:"(\\d{2})"};
1446     case "O":
1447         return {g:1,
1448             c:[
1449                 "o = results[", currentGroup, "];\n",
1450                 "var sn = o.substring(0,1);\n", // get + / - sign
1451                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1452                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1453                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1454                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1455             ].join(""),
1456             s:"([+\-]\\d{2,4})"};
1457     
1458     
1459     case "P":
1460         return {g:1,
1461                 c:[
1462                    "o = results[", currentGroup, "];\n",
1463                    "var sn = o.substring(0,1);\n",
1464                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1465                    "var mn = o.substring(4,6) % 60;\n",
1466                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1467                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1468             ].join(""),
1469             s:"([+\-]\\d{4})"};
1470     case "T":
1471         return {g:0,
1472             c:null,
1473             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1474     case "Z":
1475         return {g:1,
1476             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1477                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1478             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1479     default:
1480         return {g:0,
1481             c:null,
1482             s:String.escape(character)};
1483     }
1484 };
1485
1486 /**
1487  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1488  * @return {String} The abbreviated timezone name (e.g. 'CST')
1489  */
1490 Date.prototype.getTimezone = function() {
1491     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1492 };
1493
1494 /**
1495  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1496  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1497  */
1498 Date.prototype.getGMTOffset = function() {
1499     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1500         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1501         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1502 };
1503
1504 /**
1505  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1506  * @return {String} 2-characters representing hours and 2-characters representing minutes
1507  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1508  */
1509 Date.prototype.getGMTColonOffset = function() {
1510         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1511                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1512                 + ":"
1513                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1514 }
1515
1516 /**
1517  * Get the numeric day number of the year, adjusted for leap year.
1518  * @return {Number} 0 through 364 (365 in leap years)
1519  */
1520 Date.prototype.getDayOfYear = function() {
1521     var num = 0;
1522     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1523     for (var i = 0; i < this.getMonth(); ++i) {
1524         num += Date.daysInMonth[i];
1525     }
1526     return num + this.getDate() - 1;
1527 };
1528
1529 /**
1530  * Get the string representation of the numeric week number of the year
1531  * (equivalent to the format specifier 'W').
1532  * @return {String} '00' through '52'
1533  */
1534 Date.prototype.getWeekOfYear = function() {
1535     // Skip to Thursday of this week
1536     var now = this.getDayOfYear() + (4 - this.getDay());
1537     // Find the first Thursday of the year
1538     var jan1 = new Date(this.getFullYear(), 0, 1);
1539     var then = (7 - jan1.getDay() + 4);
1540     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1541 };
1542
1543 /**
1544  * Whether or not the current date is in a leap year.
1545  * @return {Boolean} True if the current date is in a leap year, else false
1546  */
1547 Date.prototype.isLeapYear = function() {
1548     var year = this.getFullYear();
1549     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1550 };
1551
1552 /**
1553  * Get the first day of the current month, adjusted for leap year.  The returned value
1554  * is the numeric day index within the week (0-6) which can be used in conjunction with
1555  * the {@link #monthNames} array to retrieve the textual day name.
1556  * Example:
1557  *<pre><code>
1558 var dt = new Date('1/10/2007');
1559 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1560 </code></pre>
1561  * @return {Number} The day number (0-6)
1562  */
1563 Date.prototype.getFirstDayOfMonth = function() {
1564     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1565     return (day < 0) ? (day + 7) : day;
1566 };
1567
1568 /**
1569  * Get the last day of the current month, adjusted for leap year.  The returned value
1570  * is the numeric day index within the week (0-6) which can be used in conjunction with
1571  * the {@link #monthNames} array to retrieve the textual day name.
1572  * Example:
1573  *<pre><code>
1574 var dt = new Date('1/10/2007');
1575 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1576 </code></pre>
1577  * @return {Number} The day number (0-6)
1578  */
1579 Date.prototype.getLastDayOfMonth = function() {
1580     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1581     return (day < 0) ? (day + 7) : day;
1582 };
1583
1584
1585 /**
1586  * Get the first date of this date's month
1587  * @return {Date}
1588  */
1589 Date.prototype.getFirstDateOfMonth = function() {
1590     return new Date(this.getFullYear(), this.getMonth(), 1);
1591 };
1592
1593 /**
1594  * Get the last date of this date's month
1595  * @return {Date}
1596  */
1597 Date.prototype.getLastDateOfMonth = function() {
1598     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1599 };
1600 /**
1601  * Get the number of days in the current month, adjusted for leap year.
1602  * @return {Number} The number of days in the month
1603  */
1604 Date.prototype.getDaysInMonth = function() {
1605     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1606     return Date.daysInMonth[this.getMonth()];
1607 };
1608
1609 /**
1610  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1611  * @return {String} 'st, 'nd', 'rd' or 'th'
1612  */
1613 Date.prototype.getSuffix = function() {
1614     switch (this.getDate()) {
1615         case 1:
1616         case 21:
1617         case 31:
1618             return "st";
1619         case 2:
1620         case 22:
1621             return "nd";
1622         case 3:
1623         case 23:
1624             return "rd";
1625         default:
1626             return "th";
1627     }
1628 };
1629
1630 // private
1631 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1632
1633 /**
1634  * An array of textual month names.
1635  * Override these values for international dates, for example...
1636  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1637  * @type Array
1638  * @static
1639  */
1640 Date.monthNames =
1641    ["January",
1642     "February",
1643     "March",
1644     "April",
1645     "May",
1646     "June",
1647     "July",
1648     "August",
1649     "September",
1650     "October",
1651     "November",
1652     "December"];
1653
1654 /**
1655  * An array of textual day names.
1656  * Override these values for international dates, for example...
1657  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1658  * @type Array
1659  * @static
1660  */
1661 Date.dayNames =
1662    ["Sunday",
1663     "Monday",
1664     "Tuesday",
1665     "Wednesday",
1666     "Thursday",
1667     "Friday",
1668     "Saturday"];
1669
1670 // private
1671 Date.y2kYear = 50;
1672 // private
1673 Date.monthNumbers = {
1674     Jan:0,
1675     Feb:1,
1676     Mar:2,
1677     Apr:3,
1678     May:4,
1679     Jun:5,
1680     Jul:6,
1681     Aug:7,
1682     Sep:8,
1683     Oct:9,
1684     Nov:10,
1685     Dec:11};
1686
1687 /**
1688  * Creates and returns a new Date instance with the exact same date value as the called instance.
1689  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1690  * variable will also be changed.  When the intention is to create a new variable that will not
1691  * modify the original instance, you should create a clone.
1692  *
1693  * Example of correctly cloning a date:
1694  * <pre><code>
1695 //wrong way:
1696 var orig = new Date('10/1/2006');
1697 var copy = orig;
1698 copy.setDate(5);
1699 document.write(orig);  //returns 'Thu Oct 05 2006'!
1700
1701 //correct way:
1702 var orig = new Date('10/1/2006');
1703 var copy = orig.clone();
1704 copy.setDate(5);
1705 document.write(orig);  //returns 'Thu Oct 01 2006'
1706 </code></pre>
1707  * @return {Date} The new Date instance
1708  */
1709 Date.prototype.clone = function() {
1710         return new Date(this.getTime());
1711 };
1712
1713 /**
1714  * Clears any time information from this date
1715  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1716  @return {Date} this or the clone
1717  */
1718 Date.prototype.clearTime = function(clone){
1719     if(clone){
1720         return this.clone().clearTime();
1721     }
1722     this.setHours(0);
1723     this.setMinutes(0);
1724     this.setSeconds(0);
1725     this.setMilliseconds(0);
1726     return this;
1727 };
1728
1729 // private
1730 // safari setMonth is broken -- check that this is only donw once...
1731 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1732     Date.brokenSetMonth = Date.prototype.setMonth;
1733         Date.prototype.setMonth = function(num){
1734                 if(num <= -1){
1735                         var n = Math.ceil(-num);
1736                         var back_year = Math.ceil(n/12);
1737                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1738                         this.setFullYear(this.getFullYear() - back_year);
1739                         return Date.brokenSetMonth.call(this, month);
1740                 } else {
1741                         return Date.brokenSetMonth.apply(this, arguments);
1742                 }
1743         };
1744 }
1745
1746 /** Date interval constant 
1747 * @static 
1748 * @type String */
1749 Date.MILLI = "ms";
1750 /** Date interval constant 
1751 * @static 
1752 * @type String */
1753 Date.SECOND = "s";
1754 /** Date interval constant 
1755 * @static 
1756 * @type String */
1757 Date.MINUTE = "mi";
1758 /** Date interval constant 
1759 * @static 
1760 * @type String */
1761 Date.HOUR = "h";
1762 /** Date interval constant 
1763 * @static 
1764 * @type String */
1765 Date.DAY = "d";
1766 /** Date interval constant 
1767 * @static 
1768 * @type String */
1769 Date.MONTH = "mo";
1770 /** Date interval constant 
1771 * @static 
1772 * @type String */
1773 Date.YEAR = "y";
1774
1775 /**
1776  * Provides a convenient method of performing basic date arithmetic.  This method
1777  * does not modify the Date instance being called - it creates and returns
1778  * a new Date instance containing the resulting date value.
1779  *
1780  * Examples:
1781  * <pre><code>
1782 //Basic usage:
1783 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1784 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1785
1786 //Negative values will subtract correctly:
1787 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1788 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1789
1790 //You can even chain several calls together in one line!
1791 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1792 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1793  </code></pre>
1794  *
1795  * @param {String} interval   A valid date interval enum value
1796  * @param {Number} value      The amount to add to the current date
1797  * @return {Date} The new Date instance
1798  */
1799 Date.prototype.add = function(interval, value){
1800   var d = this.clone();
1801   if (!interval || value === 0) { return d; }
1802   switch(interval.toLowerCase()){
1803     case Date.MILLI:
1804       d.setMilliseconds(this.getMilliseconds() + value);
1805       break;
1806     case Date.SECOND:
1807       d.setSeconds(this.getSeconds() + value);
1808       break;
1809     case Date.MINUTE:
1810       d.setMinutes(this.getMinutes() + value);
1811       break;
1812     case Date.HOUR:
1813       d.setHours(this.getHours() + value);
1814       break;
1815     case Date.DAY:
1816       d.setDate(this.getDate() + value);
1817       break;
1818     case Date.MONTH:
1819       var day = this.getDate();
1820       if(day > 28){
1821           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1822       }
1823       d.setDate(day);
1824       d.setMonth(this.getMonth() + value);
1825       break;
1826     case Date.YEAR:
1827       d.setFullYear(this.getFullYear() + value);
1828       break;
1829   }
1830   return d;
1831 };
1832 /*
1833  * Based on:
1834  * Ext JS Library 1.1.1
1835  * Copyright(c) 2006-2007, Ext JS, LLC.
1836  *
1837  * Originally Released Under LGPL - original licence link has changed is not relivant.
1838  *
1839  * Fork - LGPL
1840  * <script type="text/javascript">
1841  */
1842
1843 /**
1844  * @class Roo.lib.Dom
1845  * @static
1846  * 
1847  * Dom utils (from YIU afaik)
1848  * 
1849  **/
1850 Roo.lib.Dom = {
1851     /**
1852      * Get the view width
1853      * @param {Boolean} full True will get the full document, otherwise it's the view width
1854      * @return {Number} The width
1855      */
1856      
1857     getViewWidth : function(full) {
1858         return full ? this.getDocumentWidth() : this.getViewportWidth();
1859     },
1860     /**
1861      * Get the view height
1862      * @param {Boolean} full True will get the full document, otherwise it's the view height
1863      * @return {Number} The height
1864      */
1865     getViewHeight : function(full) {
1866         return full ? this.getDocumentHeight() : this.getViewportHeight();
1867     },
1868
1869     getDocumentHeight: function() {
1870         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1871         return Math.max(scrollHeight, this.getViewportHeight());
1872     },
1873
1874     getDocumentWidth: function() {
1875         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1876         return Math.max(scrollWidth, this.getViewportWidth());
1877     },
1878
1879     getViewportHeight: function() {
1880         var height = self.innerHeight;
1881         var mode = document.compatMode;
1882
1883         if ((mode || Roo.isIE) && !Roo.isOpera) {
1884             height = (mode == "CSS1Compat") ?
1885                      document.documentElement.clientHeight :
1886                      document.body.clientHeight;
1887         }
1888
1889         return height;
1890     },
1891
1892     getViewportWidth: function() {
1893         var width = self.innerWidth;
1894         var mode = document.compatMode;
1895
1896         if (mode || Roo.isIE) {
1897             width = (mode == "CSS1Compat") ?
1898                     document.documentElement.clientWidth :
1899                     document.body.clientWidth;
1900         }
1901         return width;
1902     },
1903
1904     isAncestor : function(p, c) {
1905         p = Roo.getDom(p);
1906         c = Roo.getDom(c);
1907         if (!p || !c) {
1908             return false;
1909         }
1910
1911         if (p.contains && !Roo.isSafari) {
1912             return p.contains(c);
1913         } else if (p.compareDocumentPosition) {
1914             return !!(p.compareDocumentPosition(c) & 16);
1915         } else {
1916             var parent = c.parentNode;
1917             while (parent) {
1918                 if (parent == p) {
1919                     return true;
1920                 }
1921                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1922                     return false;
1923                 }
1924                 parent = parent.parentNode;
1925             }
1926             return false;
1927         }
1928     },
1929
1930     getRegion : function(el) {
1931         return Roo.lib.Region.getRegion(el);
1932     },
1933
1934     getY : function(el) {
1935         return this.getXY(el)[1];
1936     },
1937
1938     getX : function(el) {
1939         return this.getXY(el)[0];
1940     },
1941
1942     getXY : function(el) {
1943         var p, pe, b, scroll, bd = document.body;
1944         el = Roo.getDom(el);
1945         var fly = Roo.lib.AnimBase.fly;
1946         if (el.getBoundingClientRect) {
1947             b = el.getBoundingClientRect();
1948             scroll = fly(document).getScroll();
1949             return [b.left + scroll.left, b.top + scroll.top];
1950         }
1951         var x = 0, y = 0;
1952
1953         p = el;
1954
1955         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1956
1957         while (p) {
1958
1959             x += p.offsetLeft;
1960             y += p.offsetTop;
1961
1962             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1963                 hasAbsolute = true;
1964             }
1965
1966             if (Roo.isGecko) {
1967                 pe = fly(p);
1968
1969                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1970                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1971
1972
1973                 x += bl;
1974                 y += bt;
1975
1976
1977                 if (p != el && pe.getStyle('overflow') != 'visible') {
1978                     x += bl;
1979                     y += bt;
1980                 }
1981             }
1982             p = p.offsetParent;
1983         }
1984
1985         if (Roo.isSafari && hasAbsolute) {
1986             x -= bd.offsetLeft;
1987             y -= bd.offsetTop;
1988         }
1989
1990         if (Roo.isGecko && !hasAbsolute) {
1991             var dbd = fly(bd);
1992             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1993             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1994         }
1995
1996         p = el.parentNode;
1997         while (p && p != bd) {
1998             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1999                 x -= p.scrollLeft;
2000                 y -= p.scrollTop;
2001             }
2002             p = p.parentNode;
2003         }
2004         return [x, y];
2005     },
2006  
2007   
2008
2009
2010     setXY : function(el, xy) {
2011         el = Roo.fly(el, '_setXY');
2012         el.position();
2013         var pts = el.translatePoints(xy);
2014         if (xy[0] !== false) {
2015             el.dom.style.left = pts.left + "px";
2016         }
2017         if (xy[1] !== false) {
2018             el.dom.style.top = pts.top + "px";
2019         }
2020     },
2021
2022     setX : function(el, x) {
2023         this.setXY(el, [x, false]);
2024     },
2025
2026     setY : function(el, y) {
2027         this.setXY(el, [false, y]);
2028     }
2029 };
2030 /*
2031  * Portions of this file are based on pieces of Yahoo User Interface Library
2032  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2033  * YUI licensed under the BSD License:
2034  * http://developer.yahoo.net/yui/license.txt
2035  * <script type="text/javascript">
2036  *
2037  */
2038
2039 Roo.lib.Event = function() {
2040     var loadComplete = false;
2041     var listeners = [];
2042     var unloadListeners = [];
2043     var retryCount = 0;
2044     var onAvailStack = [];
2045     var counter = 0;
2046     var lastError = null;
2047
2048     return {
2049         POLL_RETRYS: 200,
2050         POLL_INTERVAL: 20,
2051         EL: 0,
2052         TYPE: 1,
2053         FN: 2,
2054         WFN: 3,
2055         OBJ: 3,
2056         ADJ_SCOPE: 4,
2057         _interval: null,
2058
2059         startInterval: function() {
2060             if (!this._interval) {
2061                 var self = this;
2062                 var callback = function() {
2063                     self._tryPreloadAttach();
2064                 };
2065                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2066
2067             }
2068         },
2069
2070         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2071             onAvailStack.push({ id:         p_id,
2072                 fn:         p_fn,
2073                 obj:        p_obj,
2074                 override:   p_override,
2075                 checkReady: false    });
2076
2077             retryCount = this.POLL_RETRYS;
2078             this.startInterval();
2079         },
2080
2081
2082         addListener: function(el, eventName, fn) {
2083             el = Roo.getDom(el);
2084             if (!el || !fn) {
2085                 return false;
2086             }
2087
2088             if ("unload" == eventName) {
2089                 unloadListeners[unloadListeners.length] =
2090                 [el, eventName, fn];
2091                 return true;
2092             }
2093
2094             var wrappedFn = function(e) {
2095                 return fn(Roo.lib.Event.getEvent(e));
2096             };
2097
2098             var li = [el, eventName, fn, wrappedFn];
2099
2100             var index = listeners.length;
2101             listeners[index] = li;
2102
2103             this.doAdd(el, eventName, wrappedFn, false);
2104             return true;
2105
2106         },
2107
2108
2109         removeListener: function(el, eventName, fn) {
2110             var i, len;
2111
2112             el = Roo.getDom(el);
2113
2114             if(!fn) {
2115                 return this.purgeElement(el, false, eventName);
2116             }
2117
2118
2119             if ("unload" == eventName) {
2120
2121                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2122                     var li = unloadListeners[i];
2123                     if (li &&
2124                         li[0] == el &&
2125                         li[1] == eventName &&
2126                         li[2] == fn) {
2127                         unloadListeners.splice(i, 1);
2128                         return true;
2129                     }
2130                 }
2131
2132                 return false;
2133             }
2134
2135             var cacheItem = null;
2136
2137
2138             var index = arguments[3];
2139
2140             if ("undefined" == typeof index) {
2141                 index = this._getCacheIndex(el, eventName, fn);
2142             }
2143
2144             if (index >= 0) {
2145                 cacheItem = listeners[index];
2146             }
2147
2148             if (!el || !cacheItem) {
2149                 return false;
2150             }
2151
2152             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2153
2154             delete listeners[index][this.WFN];
2155             delete listeners[index][this.FN];
2156             listeners.splice(index, 1);
2157
2158             return true;
2159
2160         },
2161
2162
2163         getTarget: function(ev, resolveTextNode) {
2164             ev = ev.browserEvent || ev;
2165             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2166             var t = ev.target || ev.srcElement;
2167             return this.resolveTextNode(t);
2168         },
2169
2170
2171         resolveTextNode: function(node) {
2172             if (Roo.isSafari && node && 3 == node.nodeType) {
2173                 return node.parentNode;
2174             } else {
2175                 return node;
2176             }
2177         },
2178
2179
2180         getPageX: function(ev) {
2181             ev = ev.browserEvent || ev;
2182             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2183             var x = ev.pageX;
2184             if (!x && 0 !== x) {
2185                 x = ev.clientX || 0;
2186
2187                 if (Roo.isIE) {
2188                     x += this.getScroll()[1];
2189                 }
2190             }
2191
2192             return x;
2193         },
2194
2195
2196         getPageY: function(ev) {
2197             ev = ev.browserEvent || ev;
2198             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2199             var y = ev.pageY;
2200             if (!y && 0 !== y) {
2201                 y = ev.clientY || 0;
2202
2203                 if (Roo.isIE) {
2204                     y += this.getScroll()[0];
2205                 }
2206             }
2207
2208
2209             return y;
2210         },
2211
2212
2213         getXY: function(ev) {
2214             ev = ev.browserEvent || ev;
2215             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2216             return [this.getPageX(ev), this.getPageY(ev)];
2217         },
2218
2219
2220         getRelatedTarget: function(ev) {
2221             ev = ev.browserEvent || ev;
2222             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2223             var t = ev.relatedTarget;
2224             if (!t) {
2225                 if (ev.type == "mouseout") {
2226                     t = ev.toElement;
2227                 } else if (ev.type == "mouseover") {
2228                     t = ev.fromElement;
2229                 }
2230             }
2231
2232             return this.resolveTextNode(t);
2233         },
2234
2235
2236         getTime: function(ev) {
2237             ev = ev.browserEvent || ev;
2238             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2239             if (!ev.time) {
2240                 var t = new Date().getTime();
2241                 try {
2242                     ev.time = t;
2243                 } catch(ex) {
2244                     this.lastError = ex;
2245                     return t;
2246                 }
2247             }
2248
2249             return ev.time;
2250         },
2251
2252
2253         stopEvent: function(ev) {
2254             this.stopPropagation(ev);
2255             this.preventDefault(ev);
2256         },
2257
2258
2259         stopPropagation: function(ev) {
2260             ev = ev.browserEvent || ev;
2261             if (ev.stopPropagation) {
2262                 ev.stopPropagation();
2263             } else {
2264                 ev.cancelBubble = true;
2265             }
2266         },
2267
2268
2269         preventDefault: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             if(ev.preventDefault) {
2272                 ev.preventDefault();
2273             } else {
2274                 ev.returnValue = false;
2275             }
2276         },
2277
2278
2279         getEvent: function(e) {
2280             var ev = e || window.event;
2281             if (!ev) {
2282                 var c = this.getEvent.caller;
2283                 while (c) {
2284                     ev = c.arguments[0];
2285                     if (ev && Event == ev.constructor) {
2286                         break;
2287                     }
2288                     c = c.caller;
2289                 }
2290             }
2291             return ev;
2292         },
2293
2294
2295         getCharCode: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             return ev.charCode || ev.keyCode || 0;
2298         },
2299
2300
2301         _getCacheIndex: function(el, eventName, fn) {
2302             for (var i = 0,len = listeners.length; i < len; ++i) {
2303                 var li = listeners[i];
2304                 if (li &&
2305                     li[this.FN] == fn &&
2306                     li[this.EL] == el &&
2307                     li[this.TYPE] == eventName) {
2308                     return i;
2309                 }
2310             }
2311
2312             return -1;
2313         },
2314
2315
2316         elCache: {},
2317
2318
2319         getEl: function(id) {
2320             return document.getElementById(id);
2321         },
2322
2323
2324         clearCache: function() {
2325         },
2326
2327
2328         _load: function(e) {
2329             loadComplete = true;
2330             var EU = Roo.lib.Event;
2331
2332
2333             if (Roo.isIE) {
2334                 EU.doRemove(window, "load", EU._load);
2335             }
2336         },
2337
2338
2339         _tryPreloadAttach: function() {
2340
2341             if (this.locked) {
2342                 return false;
2343             }
2344
2345             this.locked = true;
2346
2347
2348             var tryAgain = !loadComplete;
2349             if (!tryAgain) {
2350                 tryAgain = (retryCount > 0);
2351             }
2352
2353
2354             var notAvail = [];
2355             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2356                 var item = onAvailStack[i];
2357                 if (item) {
2358                     var el = this.getEl(item.id);
2359
2360                     if (el) {
2361                         if (!item.checkReady ||
2362                             loadComplete ||
2363                             el.nextSibling ||
2364                             (document && document.body)) {
2365
2366                             var scope = el;
2367                             if (item.override) {
2368                                 if (item.override === true) {
2369                                     scope = item.obj;
2370                                 } else {
2371                                     scope = item.override;
2372                                 }
2373                             }
2374                             item.fn.call(scope, item.obj);
2375                             onAvailStack[i] = null;
2376                         }
2377                     } else {
2378                         notAvail.push(item);
2379                     }
2380                 }
2381             }
2382
2383             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2384
2385             if (tryAgain) {
2386
2387                 this.startInterval();
2388             } else {
2389                 clearInterval(this._interval);
2390                 this._interval = null;
2391             }
2392
2393             this.locked = false;
2394
2395             return true;
2396
2397         },
2398
2399
2400         purgeElement: function(el, recurse, eventName) {
2401             var elListeners = this.getListeners(el, eventName);
2402             if (elListeners) {
2403                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2404                     var l = elListeners[i];
2405                     this.removeListener(el, l.type, l.fn);
2406                 }
2407             }
2408
2409             if (recurse && el && el.childNodes) {
2410                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2411                     this.purgeElement(el.childNodes[i], recurse, eventName);
2412                 }
2413             }
2414         },
2415
2416
2417         getListeners: function(el, eventName) {
2418             var results = [], searchLists;
2419             if (!eventName) {
2420                 searchLists = [listeners, unloadListeners];
2421             } else if (eventName == "unload") {
2422                 searchLists = [unloadListeners];
2423             } else {
2424                 searchLists = [listeners];
2425             }
2426
2427             for (var j = 0; j < searchLists.length; ++j) {
2428                 var searchList = searchLists[j];
2429                 if (searchList && searchList.length > 0) {
2430                     for (var i = 0,len = searchList.length; i < len; ++i) {
2431                         var l = searchList[i];
2432                         if (l && l[this.EL] === el &&
2433                             (!eventName || eventName === l[this.TYPE])) {
2434                             results.push({
2435                                 type:   l[this.TYPE],
2436                                 fn:     l[this.FN],
2437                                 obj:    l[this.OBJ],
2438                                 adjust: l[this.ADJ_SCOPE],
2439                                 index:  i
2440                             });
2441                         }
2442                     }
2443                 }
2444             }
2445
2446             return (results.length) ? results : null;
2447         },
2448
2449
2450         _unload: function(e) {
2451
2452             var EU = Roo.lib.Event, i, j, l, len, index;
2453
2454             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2455                 l = unloadListeners[i];
2456                 if (l) {
2457                     var scope = window;
2458                     if (l[EU.ADJ_SCOPE]) {
2459                         if (l[EU.ADJ_SCOPE] === true) {
2460                             scope = l[EU.OBJ];
2461                         } else {
2462                             scope = l[EU.ADJ_SCOPE];
2463                         }
2464                     }
2465                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2466                     unloadListeners[i] = null;
2467                     l = null;
2468                     scope = null;
2469                 }
2470             }
2471
2472             unloadListeners = null;
2473
2474             if (listeners && listeners.length > 0) {
2475                 j = listeners.length;
2476                 while (j) {
2477                     index = j - 1;
2478                     l = listeners[index];
2479                     if (l) {
2480                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2481                                 l[EU.FN], index);
2482                     }
2483                     j = j - 1;
2484                 }
2485                 l = null;
2486
2487                 EU.clearCache();
2488             }
2489
2490             EU.doRemove(window, "unload", EU._unload);
2491
2492         },
2493
2494
2495         getScroll: function() {
2496             var dd = document.documentElement, db = document.body;
2497             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2498                 return [dd.scrollTop, dd.scrollLeft];
2499             } else if (db) {
2500                 return [db.scrollTop, db.scrollLeft];
2501             } else {
2502                 return [0, 0];
2503             }
2504         },
2505
2506
2507         doAdd: function () {
2508             if (window.addEventListener) {
2509                 return function(el, eventName, fn, capture) {
2510                     el.addEventListener(eventName, fn, (capture));
2511                 };
2512             } else if (window.attachEvent) {
2513                 return function(el, eventName, fn, capture) {
2514                     el.attachEvent("on" + eventName, fn);
2515                 };
2516             } else {
2517                 return function() {
2518                 };
2519             }
2520         }(),
2521
2522
2523         doRemove: function() {
2524             if (window.removeEventListener) {
2525                 return function (el, eventName, fn, capture) {
2526                     el.removeEventListener(eventName, fn, (capture));
2527                 };
2528             } else if (window.detachEvent) {
2529                 return function (el, eventName, fn) {
2530                     el.detachEvent("on" + eventName, fn);
2531                 };
2532             } else {
2533                 return function() {
2534                 };
2535             }
2536         }()
2537     };
2538     
2539 }();
2540 (function() {     
2541    
2542     var E = Roo.lib.Event;
2543     E.on = E.addListener;
2544     E.un = E.removeListener;
2545
2546     if (document && document.body) {
2547         E._load();
2548     } else {
2549         E.doAdd(window, "load", E._load);
2550     }
2551     E.doAdd(window, "unload", E._unload);
2552     E._tryPreloadAttach();
2553 })();
2554
2555 /*
2556  * Portions of this file are based on pieces of Yahoo User Interface Library
2557  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2558  * YUI licensed under the BSD License:
2559  * http://developer.yahoo.net/yui/license.txt
2560  * <script type="text/javascript">
2561  *
2562  */
2563
2564 (function() {
2565     /**
2566      * @class Roo.lib.Ajax
2567      *
2568      */
2569     Roo.lib.Ajax = {
2570         /**
2571          * @static 
2572          */
2573         request : function(method, uri, cb, data, options) {
2574             if(options){
2575                 var hs = options.headers;
2576                 if(hs){
2577                     for(var h in hs){
2578                         if(hs.hasOwnProperty(h)){
2579                             this.initHeader(h, hs[h], false);
2580                         }
2581                     }
2582                 }
2583                 if(options.xmlData){
2584                     this.initHeader('Content-Type', 'text/xml', false);
2585                     method = 'POST';
2586                     data = options.xmlData;
2587                 }
2588             }
2589
2590             return this.asyncRequest(method, uri, cb, data);
2591         },
2592
2593         serializeForm : function(form) {
2594             if(typeof form == 'string') {
2595                 form = (document.getElementById(form) || document.forms[form]);
2596             }
2597
2598             var el, name, val, disabled, data = '', hasSubmit = false;
2599             for (var i = 0; i < form.elements.length; i++) {
2600                 el = form.elements[i];
2601                 disabled = form.elements[i].disabled;
2602                 name = form.elements[i].name;
2603                 val = form.elements[i].value;
2604
2605                 if (!disabled && name){
2606                     switch (el.type)
2607                             {
2608                         case 'select-one':
2609                         case 'select-multiple':
2610                             for (var j = 0; j < el.options.length; j++) {
2611                                 if (el.options[j].selected) {
2612                                     if (Roo.isIE) {
2613                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2614                                     }
2615                                     else {
2616                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2617                                     }
2618                                 }
2619                             }
2620                             break;
2621                         case 'radio':
2622                         case 'checkbox':
2623                             if (el.checked) {
2624                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2625                             }
2626                             break;
2627                         case 'file':
2628
2629                         case undefined:
2630
2631                         case 'reset':
2632
2633                         case 'button':
2634
2635                             break;
2636                         case 'submit':
2637                             if(hasSubmit == false) {
2638                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2639                                 hasSubmit = true;
2640                             }
2641                             break;
2642                         default:
2643                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2644                             break;
2645                     }
2646                 }
2647             }
2648             data = data.substr(0, data.length - 1);
2649             return data;
2650         },
2651
2652         headers:{},
2653
2654         hasHeaders:false,
2655
2656         useDefaultHeader:true,
2657
2658         defaultPostHeader:'application/x-www-form-urlencoded',
2659
2660         useDefaultXhrHeader:true,
2661
2662         defaultXhrHeader:'XMLHttpRequest',
2663
2664         hasDefaultHeaders:true,
2665
2666         defaultHeaders:{},
2667
2668         poll:{},
2669
2670         timeout:{},
2671
2672         pollInterval:50,
2673
2674         transactionId:0,
2675
2676         setProgId:function(id)
2677         {
2678             this.activeX.unshift(id);
2679         },
2680
2681         setDefaultPostHeader:function(b)
2682         {
2683             this.useDefaultHeader = b;
2684         },
2685
2686         setDefaultXhrHeader:function(b)
2687         {
2688             this.useDefaultXhrHeader = b;
2689         },
2690
2691         setPollingInterval:function(i)
2692         {
2693             if (typeof i == 'number' && isFinite(i)) {
2694                 this.pollInterval = i;
2695             }
2696         },
2697
2698         createXhrObject:function(transactionId)
2699         {
2700             var obj,http;
2701             try
2702             {
2703
2704                 http = new XMLHttpRequest();
2705
2706                 obj = { conn:http, tId:transactionId };
2707             }
2708             catch(e)
2709             {
2710                 for (var i = 0; i < this.activeX.length; ++i) {
2711                     try
2712                     {
2713
2714                         http = new ActiveXObject(this.activeX[i]);
2715
2716                         obj = { conn:http, tId:transactionId };
2717                         break;
2718                     }
2719                     catch(e) {
2720                     }
2721                 }
2722             }
2723             finally
2724             {
2725                 return obj;
2726             }
2727         },
2728
2729         getConnectionObject:function()
2730         {
2731             var o;
2732             var tId = this.transactionId;
2733
2734             try
2735             {
2736                 o = this.createXhrObject(tId);
2737                 if (o) {
2738                     this.transactionId++;
2739                 }
2740             }
2741             catch(e) {
2742             }
2743             finally
2744             {
2745                 return o;
2746             }
2747         },
2748
2749         asyncRequest:function(method, uri, callback, postData)
2750         {
2751             var o = this.getConnectionObject();
2752
2753             if (!o) {
2754                 return null;
2755             }
2756             else {
2757                 o.conn.open(method, uri, true);
2758
2759                 if (this.useDefaultXhrHeader) {
2760                     if (!this.defaultHeaders['X-Requested-With']) {
2761                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2762                     }
2763                 }
2764
2765                 if(postData && this.useDefaultHeader){
2766                     this.initHeader('Content-Type', this.defaultPostHeader);
2767                 }
2768
2769                  if (this.hasDefaultHeaders || this.hasHeaders) {
2770                     this.setHeader(o);
2771                 }
2772
2773                 this.handleReadyState(o, callback);
2774                 o.conn.send(postData || null);
2775
2776                 return o;
2777             }
2778         },
2779
2780         handleReadyState:function(o, callback)
2781         {
2782             var oConn = this;
2783
2784             if (callback && callback.timeout) {
2785                 
2786                 this.timeout[o.tId] = window.setTimeout(function() {
2787                     oConn.abort(o, callback, true);
2788                 }, callback.timeout);
2789             }
2790
2791             this.poll[o.tId] = window.setInterval(
2792                     function() {
2793                         if (o.conn && o.conn.readyState == 4) {
2794                             window.clearInterval(oConn.poll[o.tId]);
2795                             delete oConn.poll[o.tId];
2796
2797                             if(callback && callback.timeout) {
2798                                 window.clearTimeout(oConn.timeout[o.tId]);
2799                                 delete oConn.timeout[o.tId];
2800                             }
2801
2802                             oConn.handleTransactionResponse(o, callback);
2803                         }
2804                     }
2805                     , this.pollInterval);
2806         },
2807
2808         handleTransactionResponse:function(o, callback, isAbort)
2809         {
2810
2811             if (!callback) {
2812                 this.releaseObject(o);
2813                 return;
2814             }
2815
2816             var httpStatus, responseObject;
2817
2818             try
2819             {
2820                 if (o.conn.status !== undefined && o.conn.status != 0) {
2821                     httpStatus = o.conn.status;
2822                 }
2823                 else {
2824                     httpStatus = 13030;
2825                 }
2826             }
2827             catch(e) {
2828
2829
2830                 httpStatus = 13030;
2831             }
2832
2833             if (httpStatus >= 200 && httpStatus < 300) {
2834                 responseObject = this.createResponseObject(o, callback.argument);
2835                 if (callback.success) {
2836                     if (!callback.scope) {
2837                         callback.success(responseObject);
2838                     }
2839                     else {
2840
2841
2842                         callback.success.apply(callback.scope, [responseObject]);
2843                     }
2844                 }
2845             }
2846             else {
2847                 switch (httpStatus) {
2848
2849                     case 12002:
2850                     case 12029:
2851                     case 12030:
2852                     case 12031:
2853                     case 12152:
2854                     case 13030:
2855                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2856                         if (callback.failure) {
2857                             if (!callback.scope) {
2858                                 callback.failure(responseObject);
2859                             }
2860                             else {
2861                                 callback.failure.apply(callback.scope, [responseObject]);
2862                             }
2863                         }
2864                         break;
2865                     default:
2866                         responseObject = this.createResponseObject(o, callback.argument);
2867                         if (callback.failure) {
2868                             if (!callback.scope) {
2869                                 callback.failure(responseObject);
2870                             }
2871                             else {
2872                                 callback.failure.apply(callback.scope, [responseObject]);
2873                             }
2874                         }
2875                 }
2876             }
2877
2878             this.releaseObject(o);
2879             responseObject = null;
2880         },
2881
2882         createResponseObject:function(o, callbackArg)
2883         {
2884             var obj = {};
2885             var headerObj = {};
2886
2887             try
2888             {
2889                 var headerStr = o.conn.getAllResponseHeaders();
2890                 var header = headerStr.split('\n');
2891                 for (var i = 0; i < header.length; i++) {
2892                     var delimitPos = header[i].indexOf(':');
2893                     if (delimitPos != -1) {
2894                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2895                     }
2896                 }
2897             }
2898             catch(e) {
2899             }
2900
2901             obj.tId = o.tId;
2902             obj.status = o.conn.status;
2903             obj.statusText = o.conn.statusText;
2904             obj.getResponseHeader = headerObj;
2905             obj.getAllResponseHeaders = headerStr;
2906             obj.responseText = o.conn.responseText;
2907             obj.responseXML = o.conn.responseXML;
2908
2909             if (typeof callbackArg !== undefined) {
2910                 obj.argument = callbackArg;
2911             }
2912
2913             return obj;
2914         },
2915
2916         createExceptionObject:function(tId, callbackArg, isAbort)
2917         {
2918             var COMM_CODE = 0;
2919             var COMM_ERROR = 'communication failure';
2920             var ABORT_CODE = -1;
2921             var ABORT_ERROR = 'transaction aborted';
2922
2923             var obj = {};
2924
2925             obj.tId = tId;
2926             if (isAbort) {
2927                 obj.status = ABORT_CODE;
2928                 obj.statusText = ABORT_ERROR;
2929             }
2930             else {
2931                 obj.status = COMM_CODE;
2932                 obj.statusText = COMM_ERROR;
2933             }
2934
2935             if (callbackArg) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         initHeader:function(label, value, isDefault)
2943         {
2944             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2945
2946             if (headerObj[label] === undefined) {
2947                 headerObj[label] = value;
2948             }
2949             else {
2950
2951
2952                 headerObj[label] = value + "," + headerObj[label];
2953             }
2954
2955             if (isDefault) {
2956                 this.hasDefaultHeaders = true;
2957             }
2958             else {
2959                 this.hasHeaders = true;
2960             }
2961         },
2962
2963
2964         setHeader:function(o)
2965         {
2966             if (this.hasDefaultHeaders) {
2967                 for (var prop in this.defaultHeaders) {
2968                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2969                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2970                     }
2971                 }
2972             }
2973
2974             if (this.hasHeaders) {
2975                 for (var prop in this.headers) {
2976                     if (this.headers.hasOwnProperty(prop)) {
2977                         o.conn.setRequestHeader(prop, this.headers[prop]);
2978                     }
2979                 }
2980                 this.headers = {};
2981                 this.hasHeaders = false;
2982             }
2983         },
2984
2985         resetDefaultHeaders:function() {
2986             delete this.defaultHeaders;
2987             this.defaultHeaders = {};
2988             this.hasDefaultHeaders = false;
2989         },
2990
2991         abort:function(o, callback, isTimeout)
2992         {
2993             if(this.isCallInProgress(o)) {
2994                 o.conn.abort();
2995                 window.clearInterval(this.poll[o.tId]);
2996                 delete this.poll[o.tId];
2997                 if (isTimeout) {
2998                     delete this.timeout[o.tId];
2999                 }
3000
3001                 this.handleTransactionResponse(o, callback, true);
3002
3003                 return true;
3004             }
3005             else {
3006                 return false;
3007             }
3008         },
3009
3010
3011         isCallInProgress:function(o)
3012         {
3013             if (o && o.conn) {
3014                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3015             }
3016             else {
3017
3018                 return false;
3019             }
3020         },
3021
3022
3023         releaseObject:function(o)
3024         {
3025
3026             o.conn = null;
3027
3028             o = null;
3029         },
3030
3031         activeX:[
3032         'MSXML2.XMLHTTP.3.0',
3033         'MSXML2.XMLHTTP',
3034         'Microsoft.XMLHTTP'
3035         ]
3036
3037
3038     };
3039 })();/*
3040  * Portions of this file are based on pieces of Yahoo User Interface Library
3041  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3042  * YUI licensed under the BSD License:
3043  * http://developer.yahoo.net/yui/license.txt
3044  * <script type="text/javascript">
3045  *
3046  */
3047
3048 Roo.lib.Region = function(t, r, b, l) {
3049     this.top = t;
3050     this[1] = t;
3051     this.right = r;
3052     this.bottom = b;
3053     this.left = l;
3054     this[0] = l;
3055 };
3056
3057
3058 Roo.lib.Region.prototype = {
3059     contains : function(region) {
3060         return ( region.left >= this.left &&
3061                  region.right <= this.right &&
3062                  region.top >= this.top &&
3063                  region.bottom <= this.bottom    );
3064
3065     },
3066
3067     getArea : function() {
3068         return ( (this.bottom - this.top) * (this.right - this.left) );
3069     },
3070
3071     intersect : function(region) {
3072         var t = Math.max(this.top, region.top);
3073         var r = Math.min(this.right, region.right);
3074         var b = Math.min(this.bottom, region.bottom);
3075         var l = Math.max(this.left, region.left);
3076
3077         if (b >= t && r >= l) {
3078             return new Roo.lib.Region(t, r, b, l);
3079         } else {
3080             return null;
3081         }
3082     },
3083     union : function(region) {
3084         var t = Math.min(this.top, region.top);
3085         var r = Math.max(this.right, region.right);
3086         var b = Math.max(this.bottom, region.bottom);
3087         var l = Math.min(this.left, region.left);
3088
3089         return new Roo.lib.Region(t, r, b, l);
3090     },
3091
3092     adjust : function(t, l, b, r) {
3093         this.top += t;
3094         this.left += l;
3095         this.right += r;
3096         this.bottom += b;
3097         return this;
3098     }
3099 };
3100
3101 Roo.lib.Region.getRegion = function(el) {
3102     var p = Roo.lib.Dom.getXY(el);
3103
3104     var t = p[1];
3105     var r = p[0] + el.offsetWidth;
3106     var b = p[1] + el.offsetHeight;
3107     var l = p[0];
3108
3109     return new Roo.lib.Region(t, r, b, l);
3110 };
3111 /*
3112  * Portions of this file are based on pieces of Yahoo User Interface Library
3113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3114  * YUI licensed under the BSD License:
3115  * http://developer.yahoo.net/yui/license.txt
3116  * <script type="text/javascript">
3117  *
3118  */
3119 //@@dep Roo.lib.Region
3120
3121
3122 Roo.lib.Point = function(x, y) {
3123     if (x instanceof Array) {
3124         y = x[1];
3125         x = x[0];
3126     }
3127     this.x = this.right = this.left = this[0] = x;
3128     this.y = this.top = this.bottom = this[1] = y;
3129 };
3130
3131 Roo.lib.Point.prototype = new Roo.lib.Region();
3132 /*
3133  * Portions of this file are based on pieces of Yahoo User Interface Library
3134  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3135  * YUI licensed under the BSD License:
3136  * http://developer.yahoo.net/yui/license.txt
3137  * <script type="text/javascript">
3138  *
3139  */
3140  
3141 (function() {   
3142
3143     Roo.lib.Anim = {
3144         scroll : function(el, args, duration, easing, cb, scope) {
3145             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3146         },
3147
3148         motion : function(el, args, duration, easing, cb, scope) {
3149             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3150         },
3151
3152         color : function(el, args, duration, easing, cb, scope) {
3153             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3154         },
3155
3156         run : function(el, args, duration, easing, cb, scope, type) {
3157             type = type || Roo.lib.AnimBase;
3158             if (typeof easing == "string") {
3159                 easing = Roo.lib.Easing[easing];
3160             }
3161             var anim = new type(el, args, duration, easing);
3162             anim.animateX(function() {
3163                 Roo.callback(cb, scope);
3164             });
3165             return anim;
3166         }
3167     };
3168 })();/*
3169  * Portions of this file are based on pieces of Yahoo User Interface Library
3170  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3171  * YUI licensed under the BSD License:
3172  * http://developer.yahoo.net/yui/license.txt
3173  * <script type="text/javascript">
3174  *
3175  */
3176
3177 (function() {    
3178     var libFlyweight;
3179     
3180     function fly(el) {
3181         if (!libFlyweight) {
3182             libFlyweight = new Roo.Element.Flyweight();
3183         }
3184         libFlyweight.dom = el;
3185         return libFlyweight;
3186     }
3187
3188     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3189     
3190    
3191     
3192     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3193         if (el) {
3194             this.init(el, attributes, duration, method);
3195         }
3196     };
3197
3198     Roo.lib.AnimBase.fly = fly;
3199     
3200     
3201     
3202     Roo.lib.AnimBase.prototype = {
3203
3204         toString: function() {
3205             var el = this.getEl();
3206             var id = el.id || el.tagName;
3207             return ("Anim " + id);
3208         },
3209
3210         patterns: {
3211             noNegatives:        /width|height|opacity|padding/i,
3212             offsetAttribute:  /^((width|height)|(top|left))$/,
3213             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3214             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3215         },
3216
3217
3218         doMethod: function(attr, start, end) {
3219             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3220         },
3221
3222
3223         setAttribute: function(attr, val, unit) {
3224             if (this.patterns.noNegatives.test(attr)) {
3225                 val = (val > 0) ? val : 0;
3226             }
3227
3228             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3229         },
3230
3231
3232         getAttribute: function(attr) {
3233             var el = this.getEl();
3234             var val = fly(el).getStyle(attr);
3235
3236             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3237                 return parseFloat(val);
3238             }
3239
3240             var a = this.patterns.offsetAttribute.exec(attr) || [];
3241             var pos = !!( a[3] );
3242             var box = !!( a[2] );
3243
3244
3245             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3246                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3247             } else {
3248                 val = 0;
3249             }
3250
3251             return val;
3252         },
3253
3254
3255         getDefaultUnit: function(attr) {
3256             if (this.patterns.defaultUnit.test(attr)) {
3257                 return 'px';
3258             }
3259
3260             return '';
3261         },
3262
3263         animateX : function(callback, scope) {
3264             var f = function() {
3265                 this.onComplete.removeListener(f);
3266                 if (typeof callback == "function") {
3267                     callback.call(scope || this, this);
3268                 }
3269             };
3270             this.onComplete.addListener(f, this);
3271             this.animate();
3272         },
3273
3274
3275         setRuntimeAttribute: function(attr) {
3276             var start;
3277             var end;
3278             var attributes = this.attributes;
3279
3280             this.runtimeAttributes[attr] = {};
3281
3282             var isset = function(prop) {
3283                 return (typeof prop !== 'undefined');
3284             };
3285
3286             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3287                 return false;
3288             }
3289
3290             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3291
3292
3293             if (isset(attributes[attr]['to'])) {
3294                 end = attributes[attr]['to'];
3295             } else if (isset(attributes[attr]['by'])) {
3296                 if (start.constructor == Array) {
3297                     end = [];
3298                     for (var i = 0, len = start.length; i < len; ++i) {
3299                         end[i] = start[i] + attributes[attr]['by'][i];
3300                     }
3301                 } else {
3302                     end = start + attributes[attr]['by'];
3303                 }
3304             }
3305
3306             this.runtimeAttributes[attr].start = start;
3307             this.runtimeAttributes[attr].end = end;
3308
3309
3310             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3311         },
3312
3313
3314         init: function(el, attributes, duration, method) {
3315
3316             var isAnimated = false;
3317
3318
3319             var startTime = null;
3320
3321
3322             var actualFrames = 0;
3323
3324
3325             el = Roo.getDom(el);
3326
3327
3328             this.attributes = attributes || {};
3329
3330
3331             this.duration = duration || 1;
3332
3333
3334             this.method = method || Roo.lib.Easing.easeNone;
3335
3336
3337             this.useSeconds = true;
3338
3339
3340             this.currentFrame = 0;
3341
3342
3343             this.totalFrames = Roo.lib.AnimMgr.fps;
3344
3345
3346             this.getEl = function() {
3347                 return el;
3348             };
3349
3350
3351             this.isAnimated = function() {
3352                 return isAnimated;
3353             };
3354
3355
3356             this.getStartTime = function() {
3357                 return startTime;
3358             };
3359
3360             this.runtimeAttributes = {};
3361
3362
3363             this.animate = function() {
3364                 if (this.isAnimated()) {
3365                     return false;
3366                 }
3367
3368                 this.currentFrame = 0;
3369
3370                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3371
3372                 Roo.lib.AnimMgr.registerElement(this);
3373             };
3374
3375
3376             this.stop = function(finish) {
3377                 if (finish) {
3378                     this.currentFrame = this.totalFrames;
3379                     this._onTween.fire();
3380                 }
3381                 Roo.lib.AnimMgr.stop(this);
3382             };
3383
3384             var onStart = function() {
3385                 this.onStart.fire();
3386
3387                 this.runtimeAttributes = {};
3388                 for (var attr in this.attributes) {
3389                     this.setRuntimeAttribute(attr);
3390                 }
3391
3392                 isAnimated = true;
3393                 actualFrames = 0;
3394                 startTime = new Date();
3395             };
3396
3397
3398             var onTween = function() {
3399                 var data = {
3400                     duration: new Date() - this.getStartTime(),
3401                     currentFrame: this.currentFrame
3402                 };
3403
3404                 data.toString = function() {
3405                     return (
3406                             'duration: ' + data.duration +
3407                             ', currentFrame: ' + data.currentFrame
3408                             );
3409                 };
3410
3411                 this.onTween.fire(data);
3412
3413                 var runtimeAttributes = this.runtimeAttributes;
3414
3415                 for (var attr in runtimeAttributes) {
3416                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3417                 }
3418
3419                 actualFrames += 1;
3420             };
3421
3422             var onComplete = function() {
3423                 var actual_duration = (new Date() - startTime) / 1000 ;
3424
3425                 var data = {
3426                     duration: actual_duration,
3427                     frames: actualFrames,
3428                     fps: actualFrames / actual_duration
3429                 };
3430
3431                 data.toString = function() {
3432                     return (
3433                             'duration: ' + data.duration +
3434                             ', frames: ' + data.frames +
3435                             ', fps: ' + data.fps
3436                             );
3437                 };
3438
3439                 isAnimated = false;
3440                 actualFrames = 0;
3441                 this.onComplete.fire(data);
3442             };
3443
3444
3445             this._onStart = new Roo.util.Event(this);
3446             this.onStart = new Roo.util.Event(this);
3447             this.onTween = new Roo.util.Event(this);
3448             this._onTween = new Roo.util.Event(this);
3449             this.onComplete = new Roo.util.Event(this);
3450             this._onComplete = new Roo.util.Event(this);
3451             this._onStart.addListener(onStart);
3452             this._onTween.addListener(onTween);
3453             this._onComplete.addListener(onComplete);
3454         }
3455     };
3456 })();
3457 /*
3458  * Portions of this file are based on pieces of Yahoo User Interface Library
3459  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3460  * YUI licensed under the BSD License:
3461  * http://developer.yahoo.net/yui/license.txt
3462  * <script type="text/javascript">
3463  *
3464  */
3465
3466 Roo.lib.AnimMgr = new function() {
3467
3468     var thread = null;
3469
3470
3471     var queue = [];
3472
3473
3474     var tweenCount = 0;
3475
3476
3477     this.fps = 1000;
3478
3479
3480     this.delay = 1;
3481
3482
3483     this.registerElement = function(tween) {
3484         queue[queue.length] = tween;
3485         tweenCount += 1;
3486         tween._onStart.fire();
3487         this.start();
3488     };
3489
3490
3491     this.unRegister = function(tween, index) {
3492         tween._onComplete.fire();
3493         index = index || getIndex(tween);
3494         if (index != -1) {
3495             queue.splice(index, 1);
3496         }
3497
3498         tweenCount -= 1;
3499         if (tweenCount <= 0) {
3500             this.stop();
3501         }
3502     };
3503
3504
3505     this.start = function() {
3506         if (thread === null) {
3507             thread = setInterval(this.run, this.delay);
3508         }
3509     };
3510
3511
3512     this.stop = function(tween) {
3513         if (!tween) {
3514             clearInterval(thread);
3515
3516             for (var i = 0, len = queue.length; i < len; ++i) {
3517                 if (queue[0].isAnimated()) {
3518                     this.unRegister(queue[0], 0);
3519                 }
3520             }
3521
3522             queue = [];
3523             thread = null;
3524             tweenCount = 0;
3525         }
3526         else {
3527             this.unRegister(tween);
3528         }
3529     };
3530
3531
3532     this.run = function() {
3533         for (var i = 0, len = queue.length; i < len; ++i) {
3534             var tween = queue[i];
3535             if (!tween || !tween.isAnimated()) {
3536                 continue;
3537             }
3538
3539             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3540             {
3541                 tween.currentFrame += 1;
3542
3543                 if (tween.useSeconds) {
3544                     correctFrame(tween);
3545                 }
3546                 tween._onTween.fire();
3547             }
3548             else {
3549                 Roo.lib.AnimMgr.stop(tween, i);
3550             }
3551         }
3552     };
3553
3554     var getIndex = function(anim) {
3555         for (var i = 0, len = queue.length; i < len; ++i) {
3556             if (queue[i] == anim) {
3557                 return i;
3558             }
3559         }
3560         return -1;
3561     };
3562
3563
3564     var correctFrame = function(tween) {
3565         var frames = tween.totalFrames;
3566         var frame = tween.currentFrame;
3567         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3568         var elapsed = (new Date() - tween.getStartTime());
3569         var tweak = 0;
3570
3571         if (elapsed < tween.duration * 1000) {
3572             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3573         } else {
3574             tweak = frames - (frame + 1);
3575         }
3576         if (tweak > 0 && isFinite(tweak)) {
3577             if (tween.currentFrame + tweak >= frames) {
3578                 tweak = frames - (frame + 1);
3579             }
3580
3581             tween.currentFrame += tweak;
3582         }
3583     };
3584 };
3585
3586     /*
3587  * Portions of this file are based on pieces of Yahoo User Interface Library
3588  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3589  * YUI licensed under the BSD License:
3590  * http://developer.yahoo.net/yui/license.txt
3591  * <script type="text/javascript">
3592  *
3593  */
3594 Roo.lib.Bezier = new function() {
3595
3596         this.getPosition = function(points, t) {
3597             var n = points.length;
3598             var tmp = [];
3599
3600             for (var i = 0; i < n; ++i) {
3601                 tmp[i] = [points[i][0], points[i][1]];
3602             }
3603
3604             for (var j = 1; j < n; ++j) {
3605                 for (i = 0; i < n - j; ++i) {
3606                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3607                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3608                 }
3609             }
3610
3611             return [ tmp[0][0], tmp[0][1] ];
3612
3613         };
3614     };/*
3615  * Portions of this file are based on pieces of Yahoo User Interface Library
3616  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3617  * YUI licensed under the BSD License:
3618  * http://developer.yahoo.net/yui/license.txt
3619  * <script type="text/javascript">
3620  *
3621  */
3622 (function() {
3623
3624     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3625         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3626     };
3627
3628     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3629
3630     var fly = Roo.lib.AnimBase.fly;
3631     var Y = Roo.lib;
3632     var superclass = Y.ColorAnim.superclass;
3633     var proto = Y.ColorAnim.prototype;
3634
3635     proto.toString = function() {
3636         var el = this.getEl();
3637         var id = el.id || el.tagName;
3638         return ("ColorAnim " + id);
3639     };
3640
3641     proto.patterns.color = /color$/i;
3642     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3643     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3644     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3645     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3646
3647
3648     proto.parseColor = function(s) {
3649         if (s.length == 3) {
3650             return s;
3651         }
3652
3653         var c = this.patterns.hex.exec(s);
3654         if (c && c.length == 4) {
3655             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3656         }
3657
3658         c = this.patterns.rgb.exec(s);
3659         if (c && c.length == 4) {
3660             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3661         }
3662
3663         c = this.patterns.hex3.exec(s);
3664         if (c && c.length == 4) {
3665             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3666         }
3667
3668         return null;
3669     };
3670     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3671     proto.getAttribute = function(attr) {
3672         var el = this.getEl();
3673         if (this.patterns.color.test(attr)) {
3674             var val = fly(el).getStyle(attr);
3675
3676             if (this.patterns.transparent.test(val)) {
3677                 var parent = el.parentNode;
3678                 val = fly(parent).getStyle(attr);
3679
3680                 while (parent && this.patterns.transparent.test(val)) {
3681                     parent = parent.parentNode;
3682                     val = fly(parent).getStyle(attr);
3683                     if (parent.tagName.toUpperCase() == 'HTML') {
3684                         val = '#fff';
3685                     }
3686                 }
3687             }
3688         } else {
3689             val = superclass.getAttribute.call(this, attr);
3690         }
3691
3692         return val;
3693     };
3694     proto.getAttribute = function(attr) {
3695         var el = this.getEl();
3696         if (this.patterns.color.test(attr)) {
3697             var val = fly(el).getStyle(attr);
3698
3699             if (this.patterns.transparent.test(val)) {
3700                 var parent = el.parentNode;
3701                 val = fly(parent).getStyle(attr);
3702
3703                 while (parent && this.patterns.transparent.test(val)) {
3704                     parent = parent.parentNode;
3705                     val = fly(parent).getStyle(attr);
3706                     if (parent.tagName.toUpperCase() == 'HTML') {
3707                         val = '#fff';
3708                     }
3709                 }
3710             }
3711         } else {
3712             val = superclass.getAttribute.call(this, attr);
3713         }
3714
3715         return val;
3716     };
3717
3718     proto.doMethod = function(attr, start, end) {
3719         var val;
3720
3721         if (this.patterns.color.test(attr)) {
3722             val = [];
3723             for (var i = 0, len = start.length; i < len; ++i) {
3724                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3725             }
3726
3727             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3728         }
3729         else {
3730             val = superclass.doMethod.call(this, attr, start, end);
3731         }
3732
3733         return val;
3734     };
3735
3736     proto.setRuntimeAttribute = function(attr) {
3737         superclass.setRuntimeAttribute.call(this, attr);
3738
3739         if (this.patterns.color.test(attr)) {
3740             var attributes = this.attributes;
3741             var start = this.parseColor(this.runtimeAttributes[attr].start);
3742             var end = this.parseColor(this.runtimeAttributes[attr].end);
3743
3744             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3745                 end = this.parseColor(attributes[attr].by);
3746
3747                 for (var i = 0, len = start.length; i < len; ++i) {
3748                     end[i] = start[i] + end[i];
3749                 }
3750             }
3751
3752             this.runtimeAttributes[attr].start = start;
3753             this.runtimeAttributes[attr].end = end;
3754         }
3755     };
3756 })();
3757
3758 /*
3759  * Portions of this file are based on pieces of Yahoo User Interface Library
3760  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3761  * YUI licensed under the BSD License:
3762  * http://developer.yahoo.net/yui/license.txt
3763  * <script type="text/javascript">
3764  *
3765  */
3766 Roo.lib.Easing = {
3767
3768
3769     easeNone: function (t, b, c, d) {
3770         return c * t / d + b;
3771     },
3772
3773
3774     easeIn: function (t, b, c, d) {
3775         return c * (t /= d) * t + b;
3776     },
3777
3778
3779     easeOut: function (t, b, c, d) {
3780         return -c * (t /= d) * (t - 2) + b;
3781     },
3782
3783
3784     easeBoth: function (t, b, c, d) {
3785         if ((t /= d / 2) < 1) {
3786             return c / 2 * t * t + b;
3787         }
3788
3789         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3790     },
3791
3792
3793     easeInStrong: function (t, b, c, d) {
3794         return c * (t /= d) * t * t * t + b;
3795     },
3796
3797
3798     easeOutStrong: function (t, b, c, d) {
3799         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3800     },
3801
3802
3803     easeBothStrong: function (t, b, c, d) {
3804         if ((t /= d / 2) < 1) {
3805             return c / 2 * t * t * t * t + b;
3806         }
3807
3808         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3809     },
3810
3811
3812
3813     elasticIn: function (t, b, c, d, a, p) {
3814         if (t == 0) {
3815             return b;
3816         }
3817         if ((t /= d) == 1) {
3818             return b + c;
3819         }
3820         if (!p) {
3821             p = d * .3;
3822         }
3823
3824         if (!a || a < Math.abs(c)) {
3825             a = c;
3826             var s = p / 4;
3827         }
3828         else {
3829             var s = p / (2 * Math.PI) * Math.asin(c / a);
3830         }
3831
3832         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3833     },
3834
3835
3836     elasticOut: function (t, b, c, d, a, p) {
3837         if (t == 0) {
3838             return b;
3839         }
3840         if ((t /= d) == 1) {
3841             return b + c;
3842         }
3843         if (!p) {
3844             p = d * .3;
3845         }
3846
3847         if (!a || a < Math.abs(c)) {
3848             a = c;
3849             var s = p / 4;
3850         }
3851         else {
3852             var s = p / (2 * Math.PI) * Math.asin(c / a);
3853         }
3854
3855         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3856     },
3857
3858
3859     elasticBoth: function (t, b, c, d, a, p) {
3860         if (t == 0) {
3861             return b;
3862         }
3863
3864         if ((t /= d / 2) == 2) {
3865             return b + c;
3866         }
3867
3868         if (!p) {
3869             p = d * (.3 * 1.5);
3870         }
3871
3872         if (!a || a < Math.abs(c)) {
3873             a = c;
3874             var s = p / 4;
3875         }
3876         else {
3877             var s = p / (2 * Math.PI) * Math.asin(c / a);
3878         }
3879
3880         if (t < 1) {
3881             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3882                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3883         }
3884         return a * Math.pow(2, -10 * (t -= 1)) *
3885                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3886     },
3887
3888
3889
3890     backIn: function (t, b, c, d, s) {
3891         if (typeof s == 'undefined') {
3892             s = 1.70158;
3893         }
3894         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3895     },
3896
3897
3898     backOut: function (t, b, c, d, s) {
3899         if (typeof s == 'undefined') {
3900             s = 1.70158;
3901         }
3902         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3903     },
3904
3905
3906     backBoth: function (t, b, c, d, s) {
3907         if (typeof s == 'undefined') {
3908             s = 1.70158;
3909         }
3910
3911         if ((t /= d / 2 ) < 1) {
3912             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3913         }
3914         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3915     },
3916
3917
3918     bounceIn: function (t, b, c, d) {
3919         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3920     },
3921
3922
3923     bounceOut: function (t, b, c, d) {
3924         if ((t /= d) < (1 / 2.75)) {
3925             return c * (7.5625 * t * t) + b;
3926         } else if (t < (2 / 2.75)) {
3927             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3928         } else if (t < (2.5 / 2.75)) {
3929             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3930         }
3931         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3932     },
3933
3934
3935     bounceBoth: function (t, b, c, d) {
3936         if (t < d / 2) {
3937             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3938         }
3939         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3940     }
3941 };/*
3942  * Portions of this file are based on pieces of Yahoo User Interface Library
3943  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3944  * YUI licensed under the BSD License:
3945  * http://developer.yahoo.net/yui/license.txt
3946  * <script type="text/javascript">
3947  *
3948  */
3949     (function() {
3950         Roo.lib.Motion = function(el, attributes, duration, method) {
3951             if (el) {
3952                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3953             }
3954         };
3955
3956         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3957
3958
3959         var Y = Roo.lib;
3960         var superclass = Y.Motion.superclass;
3961         var proto = Y.Motion.prototype;
3962
3963         proto.toString = function() {
3964             var el = this.getEl();
3965             var id = el.id || el.tagName;
3966             return ("Motion " + id);
3967         };
3968
3969         proto.patterns.points = /^points$/i;
3970
3971         proto.setAttribute = function(attr, val, unit) {
3972             if (this.patterns.points.test(attr)) {
3973                 unit = unit || 'px';
3974                 superclass.setAttribute.call(this, 'left', val[0], unit);
3975                 superclass.setAttribute.call(this, 'top', val[1], unit);
3976             } else {
3977                 superclass.setAttribute.call(this, attr, val, unit);
3978             }
3979         };
3980
3981         proto.getAttribute = function(attr) {
3982             if (this.patterns.points.test(attr)) {
3983                 var val = [
3984                         superclass.getAttribute.call(this, 'left'),
3985                         superclass.getAttribute.call(this, 'top')
3986                         ];
3987             } else {
3988                 val = superclass.getAttribute.call(this, attr);
3989             }
3990
3991             return val;
3992         };
3993
3994         proto.doMethod = function(attr, start, end) {
3995             var val = null;
3996
3997             if (this.patterns.points.test(attr)) {
3998                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3999                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4000             } else {
4001                 val = superclass.doMethod.call(this, attr, start, end);
4002             }
4003             return val;
4004         };
4005
4006         proto.setRuntimeAttribute = function(attr) {
4007             if (this.patterns.points.test(attr)) {
4008                 var el = this.getEl();
4009                 var attributes = this.attributes;
4010                 var start;
4011                 var control = attributes['points']['control'] || [];
4012                 var end;
4013                 var i, len;
4014
4015                 if (control.length > 0 && !(control[0] instanceof Array)) {
4016                     control = [control];
4017                 } else {
4018                     var tmp = [];
4019                     for (i = 0,len = control.length; i < len; ++i) {
4020                         tmp[i] = control[i];
4021                     }
4022                     control = tmp;
4023                 }
4024
4025                 Roo.fly(el).position();
4026
4027                 if (isset(attributes['points']['from'])) {
4028                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4029                 }
4030                 else {
4031                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4032                 }
4033
4034                 start = this.getAttribute('points');
4035
4036
4037                 if (isset(attributes['points']['to'])) {
4038                     end = translateValues.call(this, attributes['points']['to'], start);
4039
4040                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4041                     for (i = 0,len = control.length; i < len; ++i) {
4042                         control[i] = translateValues.call(this, control[i], start);
4043                     }
4044
4045
4046                 } else if (isset(attributes['points']['by'])) {
4047                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4048
4049                     for (i = 0,len = control.length; i < len; ++i) {
4050                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4051                     }
4052                 }
4053
4054                 this.runtimeAttributes[attr] = [start];
4055
4056                 if (control.length > 0) {
4057                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4058                 }
4059
4060                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4061             }
4062             else {
4063                 superclass.setRuntimeAttribute.call(this, attr);
4064             }
4065         };
4066
4067         var translateValues = function(val, start) {
4068             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4069             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4070
4071             return val;
4072         };
4073
4074         var isset = function(prop) {
4075             return (typeof prop !== 'undefined');
4076         };
4077     })();
4078 /*
4079  * Portions of this file are based on pieces of Yahoo User Interface Library
4080  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4081  * YUI licensed under the BSD License:
4082  * http://developer.yahoo.net/yui/license.txt
4083  * <script type="text/javascript">
4084  *
4085  */
4086     (function() {
4087         Roo.lib.Scroll = function(el, attributes, duration, method) {
4088             if (el) {
4089                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4090             }
4091         };
4092
4093         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4094
4095
4096         var Y = Roo.lib;
4097         var superclass = Y.Scroll.superclass;
4098         var proto = Y.Scroll.prototype;
4099
4100         proto.toString = function() {
4101             var el = this.getEl();
4102             var id = el.id || el.tagName;
4103             return ("Scroll " + id);
4104         };
4105
4106         proto.doMethod = function(attr, start, end) {
4107             var val = null;
4108
4109             if (attr == 'scroll') {
4110                 val = [
4111                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4112                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4113                         ];
4114
4115             } else {
4116                 val = superclass.doMethod.call(this, attr, start, end);
4117             }
4118             return val;
4119         };
4120
4121         proto.getAttribute = function(attr) {
4122             var val = null;
4123             var el = this.getEl();
4124
4125             if (attr == 'scroll') {
4126                 val = [ el.scrollLeft, el.scrollTop ];
4127             } else {
4128                 val = superclass.getAttribute.call(this, attr);
4129             }
4130
4131             return val;
4132         };
4133
4134         proto.setAttribute = function(attr, val, unit) {
4135             var el = this.getEl();
4136
4137             if (attr == 'scroll') {
4138                 el.scrollLeft = val[0];
4139                 el.scrollTop = val[1];
4140             } else {
4141                 superclass.setAttribute.call(this, attr, val, unit);
4142             }
4143         };
4144     })();
4145 /*
4146  * Based on:
4147  * Ext JS Library 1.1.1
4148  * Copyright(c) 2006-2007, Ext JS, LLC.
4149  *
4150  * Originally Released Under LGPL - original licence link has changed is not relivant.
4151  *
4152  * Fork - LGPL
4153  * <script type="text/javascript">
4154  */
4155
4156
4157 // nasty IE9 hack - what a pile of crap that is..
4158
4159  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4160     Range.prototype.createContextualFragment = function (html) {
4161         var doc = window.document;
4162         var container = doc.createElement("div");
4163         container.innerHTML = html;
4164         var frag = doc.createDocumentFragment(), n;
4165         while ((n = container.firstChild)) {
4166             frag.appendChild(n);
4167         }
4168         return frag;
4169     };
4170 }
4171
4172 /**
4173  * @class Roo.DomHelper
4174  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4175  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4176  * @singleton
4177  */
4178 Roo.DomHelper = function(){
4179     var tempTableEl = null;
4180     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4181     var tableRe = /^table|tbody|tr|td$/i;
4182     var xmlns = {};
4183     // build as innerHTML where available
4184     /** @ignore */
4185     var createHtml = function(o){
4186         if(typeof o == 'string'){
4187             return o;
4188         }
4189         var b = "";
4190         if(!o.tag){
4191             o.tag = "div";
4192         }
4193         b += "<" + o.tag;
4194         for(var attr in o){
4195             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4196             if(attr == "style"){
4197                 var s = o["style"];
4198                 if(typeof s == "function"){
4199                     s = s.call();
4200                 }
4201                 if(typeof s == "string"){
4202                     b += ' style="' + s + '"';
4203                 }else if(typeof s == "object"){
4204                     b += ' style="';
4205                     for(var key in s){
4206                         if(typeof s[key] != "function"){
4207                             b += key + ":" + s[key] + ";";
4208                         }
4209                     }
4210                     b += '"';
4211                 }
4212             }else{
4213                 if(attr == "cls"){
4214                     b += ' class="' + o["cls"] + '"';
4215                 }else if(attr == "htmlFor"){
4216                     b += ' for="' + o["htmlFor"] + '"';
4217                 }else{
4218                     b += " " + attr + '="' + o[attr] + '"';
4219                 }
4220             }
4221         }
4222         if(emptyTags.test(o.tag)){
4223             b += "/>";
4224         }else{
4225             b += ">";
4226             var cn = o.children || o.cn;
4227             if(cn){
4228                 //http://bugs.kde.org/show_bug.cgi?id=71506
4229                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4230                     for(var i = 0, len = cn.length; i < len; i++) {
4231                         b += createHtml(cn[i], b);
4232                     }
4233                 }else{
4234                     b += createHtml(cn, b);
4235                 }
4236             }
4237             if(o.html){
4238                 b += o.html;
4239             }
4240             b += "</" + o.tag + ">";
4241         }
4242         return b;
4243     };
4244
4245     // build as dom
4246     /** @ignore */
4247     var createDom = function(o, parentNode){
4248          
4249         // defininition craeted..
4250         var ns = false;
4251         if (o.ns && o.ns != 'html') {
4252                
4253             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4254                 xmlns[o.ns] = o.xmlns;
4255                 ns = o.xmlns;
4256             }
4257             if (typeof(xmlns[o.ns]) == 'undefined') {
4258                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4259             }
4260             ns = xmlns[o.ns];
4261         }
4262         
4263         
4264         if (typeof(o) == 'string') {
4265             return parentNode.appendChild(document.createTextNode(o));
4266         }
4267         o.tag = o.tag || div;
4268         if (o.ns && Roo.isIE) {
4269             ns = false;
4270             o.tag = o.ns + ':' + o.tag;
4271             
4272         }
4273         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4274         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4275         for(var attr in o){
4276             
4277             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4278                     attr == "style" || typeof o[attr] == "function") { continue; }
4279                     
4280             if(attr=="cls" && Roo.isIE){
4281                 el.className = o["cls"];
4282             }else{
4283                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4284                 else { 
4285                     el[attr] = o[attr];
4286                 }
4287             }
4288         }
4289         Roo.DomHelper.applyStyles(el, o.style);
4290         var cn = o.children || o.cn;
4291         if(cn){
4292             //http://bugs.kde.org/show_bug.cgi?id=71506
4293              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4294                 for(var i = 0, len = cn.length; i < len; i++) {
4295                     createDom(cn[i], el);
4296                 }
4297             }else{
4298                 createDom(cn, el);
4299             }
4300         }
4301         if(o.html){
4302             el.innerHTML = o.html;
4303         }
4304         if(parentNode){
4305            parentNode.appendChild(el);
4306         }
4307         return el;
4308     };
4309
4310     var ieTable = function(depth, s, h, e){
4311         tempTableEl.innerHTML = [s, h, e].join('');
4312         var i = -1, el = tempTableEl;
4313         while(++i < depth){
4314             el = el.firstChild;
4315         }
4316         return el;
4317     };
4318
4319     // kill repeat to save bytes
4320     var ts = '<table>',
4321         te = '</table>',
4322         tbs = ts+'<tbody>',
4323         tbe = '</tbody>'+te,
4324         trs = tbs + '<tr>',
4325         tre = '</tr>'+tbe;
4326
4327     /**
4328      * @ignore
4329      * Nasty code for IE's broken table implementation
4330      */
4331     var insertIntoTable = function(tag, where, el, html){
4332         if(!tempTableEl){
4333             tempTableEl = document.createElement('div');
4334         }
4335         var node;
4336         var before = null;
4337         if(tag == 'td'){
4338             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4339                 return;
4340             }
4341             if(where == 'beforebegin'){
4342                 before = el;
4343                 el = el.parentNode;
4344             } else{
4345                 before = el.nextSibling;
4346                 el = el.parentNode;
4347             }
4348             node = ieTable(4, trs, html, tre);
4349         }
4350         else if(tag == 'tr'){
4351             if(where == 'beforebegin'){
4352                 before = el;
4353                 el = el.parentNode;
4354                 node = ieTable(3, tbs, html, tbe);
4355             } else if(where == 'afterend'){
4356                 before = el.nextSibling;
4357                 el = el.parentNode;
4358                 node = ieTable(3, tbs, html, tbe);
4359             } else{ // INTO a TR
4360                 if(where == 'afterbegin'){
4361                     before = el.firstChild;
4362                 }
4363                 node = ieTable(4, trs, html, tre);
4364             }
4365         } else if(tag == 'tbody'){
4366             if(where == 'beforebegin'){
4367                 before = el;
4368                 el = el.parentNode;
4369                 node = ieTable(2, ts, html, te);
4370             } else if(where == 'afterend'){
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373                 node = ieTable(2, ts, html, te);
4374             } else{
4375                 if(where == 'afterbegin'){
4376                     before = el.firstChild;
4377                 }
4378                 node = ieTable(3, tbs, html, tbe);
4379             }
4380         } else{ // TABLE
4381             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4382                 return;
4383             }
4384             if(where == 'afterbegin'){
4385                 before = el.firstChild;
4386             }
4387             node = ieTable(2, ts, html, te);
4388         }
4389         el.insertBefore(node, before);
4390         return node;
4391     };
4392
4393     return {
4394     /** True to force the use of DOM instead of html fragments @type Boolean */
4395     useDom : false,
4396
4397     /**
4398      * Returns the markup for the passed Element(s) config
4399      * @param {Object} o The Dom object spec (and children)
4400      * @return {String}
4401      */
4402     markup : function(o){
4403         return createHtml(o);
4404     },
4405
4406     /**
4407      * Applies a style specification to an element
4408      * @param {String/HTMLElement} el The element to apply styles to
4409      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4410      * a function which returns such a specification.
4411      */
4412     applyStyles : function(el, styles){
4413         if(styles){
4414            el = Roo.fly(el);
4415            if(typeof styles == "string"){
4416                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4417                var matches;
4418                while ((matches = re.exec(styles)) != null){
4419                    el.setStyle(matches[1], matches[2]);
4420                }
4421            }else if (typeof styles == "object"){
4422                for (var style in styles){
4423                   el.setStyle(style, styles[style]);
4424                }
4425            }else if (typeof styles == "function"){
4426                 Roo.DomHelper.applyStyles(el, styles.call());
4427            }
4428         }
4429     },
4430
4431     /**
4432      * Inserts an HTML fragment into the Dom
4433      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4434      * @param {HTMLElement} el The context element
4435      * @param {String} html The HTML fragmenet
4436      * @return {HTMLElement} The new node
4437      */
4438     insertHtml : function(where, el, html){
4439         where = where.toLowerCase();
4440         if(el.insertAdjacentHTML){
4441             if(tableRe.test(el.tagName)){
4442                 var rs;
4443                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4444                     return rs;
4445                 }
4446             }
4447             switch(where){
4448                 case "beforebegin":
4449                     el.insertAdjacentHTML('BeforeBegin', html);
4450                     return el.previousSibling;
4451                 case "afterbegin":
4452                     el.insertAdjacentHTML('AfterBegin', html);
4453                     return el.firstChild;
4454                 case "beforeend":
4455                     el.insertAdjacentHTML('BeforeEnd', html);
4456                     return el.lastChild;
4457                 case "afterend":
4458                     el.insertAdjacentHTML('AfterEnd', html);
4459                     return el.nextSibling;
4460             }
4461             throw 'Illegal insertion point -> "' + where + '"';
4462         }
4463         var range = el.ownerDocument.createRange();
4464         var frag;
4465         switch(where){
4466              case "beforebegin":
4467                 range.setStartBefore(el);
4468                 frag = range.createContextualFragment(html);
4469                 el.parentNode.insertBefore(frag, el);
4470                 return el.previousSibling;
4471              case "afterbegin":
4472                 if(el.firstChild){
4473                     range.setStartBefore(el.firstChild);
4474                     frag = range.createContextualFragment(html);
4475                     el.insertBefore(frag, el.firstChild);
4476                     return el.firstChild;
4477                 }else{
4478                     el.innerHTML = html;
4479                     return el.firstChild;
4480                 }
4481             case "beforeend":
4482                 if(el.lastChild){
4483                     range.setStartAfter(el.lastChild);
4484                     frag = range.createContextualFragment(html);
4485                     el.appendChild(frag);
4486                     return el.lastChild;
4487                 }else{
4488                     el.innerHTML = html;
4489                     return el.lastChild;
4490                 }
4491             case "afterend":
4492                 range.setStartAfter(el);
4493                 frag = range.createContextualFragment(html);
4494                 el.parentNode.insertBefore(frag, el.nextSibling);
4495                 return el.nextSibling;
4496             }
4497             throw 'Illegal insertion point -> "' + where + '"';
4498     },
4499
4500     /**
4501      * Creates new Dom element(s) and inserts them before el
4502      * @param {String/HTMLElement/Element} el The context element
4503      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4504      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4505      * @return {HTMLElement/Roo.Element} The new node
4506      */
4507     insertBefore : function(el, o, returnElement){
4508         return this.doInsert(el, o, returnElement, "beforeBegin");
4509     },
4510
4511     /**
4512      * Creates new Dom element(s) and inserts them after el
4513      * @param {String/HTMLElement/Element} el The context element
4514      * @param {Object} o The Dom object spec (and children)
4515      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4516      * @return {HTMLElement/Roo.Element} The new node
4517      */
4518     insertAfter : function(el, o, returnElement){
4519         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4520     },
4521
4522     /**
4523      * Creates new Dom element(s) and inserts them as the first child of el
4524      * @param {String/HTMLElement/Element} el The context element
4525      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4526      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4527      * @return {HTMLElement/Roo.Element} The new node
4528      */
4529     insertFirst : function(el, o, returnElement){
4530         return this.doInsert(el, o, returnElement, "afterBegin");
4531     },
4532
4533     // private
4534     doInsert : function(el, o, returnElement, pos, sibling){
4535         el = Roo.getDom(el);
4536         var newNode;
4537         if(this.useDom || o.ns){
4538             newNode = createDom(o, null);
4539             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4540         }else{
4541             var html = createHtml(o);
4542             newNode = this.insertHtml(pos, el, html);
4543         }
4544         return returnElement ? Roo.get(newNode, true) : newNode;
4545     },
4546
4547     /**
4548      * Creates new Dom element(s) and appends them to el
4549      * @param {String/HTMLElement/Element} el The context element
4550      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4551      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4552      * @return {HTMLElement/Roo.Element} The new node
4553      */
4554     append : function(el, o, returnElement){
4555         el = Roo.getDom(el);
4556         var newNode;
4557         if(this.useDom || o.ns){
4558             newNode = createDom(o, null);
4559             el.appendChild(newNode);
4560         }else{
4561             var html = createHtml(o);
4562             newNode = this.insertHtml("beforeEnd", el, html);
4563         }
4564         return returnElement ? Roo.get(newNode, true) : newNode;
4565     },
4566
4567     /**
4568      * Creates new Dom element(s) and overwrites the contents of el with them
4569      * @param {String/HTMLElement/Element} el The context element
4570      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4571      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4572      * @return {HTMLElement/Roo.Element} The new node
4573      */
4574     overwrite : function(el, o, returnElement){
4575         el = Roo.getDom(el);
4576         if (o.ns) {
4577           
4578             while (el.childNodes.length) {
4579                 el.removeChild(el.firstChild);
4580             }
4581             createDom(o, el);
4582         } else {
4583             el.innerHTML = createHtml(o);   
4584         }
4585         
4586         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4587     },
4588
4589     /**
4590      * Creates a new Roo.DomHelper.Template from the Dom object spec
4591      * @param {Object} o The Dom object spec (and children)
4592      * @return {Roo.DomHelper.Template} The new template
4593      */
4594     createTemplate : function(o){
4595         var html = createHtml(o);
4596         return new Roo.Template(html);
4597     }
4598     };
4599 }();
4600 /*
4601  * Based on:
4602  * Ext JS Library 1.1.1
4603  * Copyright(c) 2006-2007, Ext JS, LLC.
4604  *
4605  * Originally Released Under LGPL - original licence link has changed is not relivant.
4606  *
4607  * Fork - LGPL
4608  * <script type="text/javascript">
4609  */
4610  
4611 /**
4612 * @class Roo.Template
4613 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4614 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4615 * Usage:
4616 <pre><code>
4617 var t = new Roo.Template({
4618     html :  '&lt;div name="{id}"&gt;' + 
4619         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4620         '&lt;/div&gt;',
4621     myformat: function (value, allValues) {
4622         return 'XX' + value;
4623     }
4624 });
4625 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4626 </code></pre>
4627 * For more information see this blog post with examples:
4628 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4629      - Create Elements using DOM, HTML fragments and Templates</a>. 
4630 * @constructor
4631 * @param {Object} cfg - Configuration object.
4632 */
4633 Roo.Template = function(cfg){
4634     // BC!
4635     if(cfg instanceof Array){
4636         cfg = cfg.join("");
4637     }else if(arguments.length > 1){
4638         cfg = Array.prototype.join.call(arguments, "");
4639     }
4640     
4641     
4642     if (typeof(cfg) == 'object') {
4643         Roo.apply(this,cfg)
4644     } else {
4645         // bc
4646         this.html = cfg;
4647     }
4648     if (this.url) {
4649         this.load();
4650     }
4651     
4652 };
4653 Roo.Template.prototype = {
4654     
4655     /**
4656      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4657      *                    it should be fixed so that template is observable...
4658      */
4659     url : false,
4660     /**
4661      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4662      */
4663     html : '',
4664     /**
4665      * Returns an HTML fragment of this template with the specified values applied.
4666      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4667      * @return {String} The HTML fragment
4668      */
4669     applyTemplate : function(values){
4670         Roo.log(["applyTemplate", values]);
4671         try {
4672            
4673             if(this.compiled){
4674                 return this.compiled(values);
4675             }
4676             var useF = this.disableFormats !== true;
4677             var fm = Roo.util.Format, tpl = this;
4678             var fn = function(m, name, format, args){
4679                 if(format && useF){
4680                     if(format.substr(0, 5) == "this."){
4681                         return tpl.call(format.substr(5), values[name], values);
4682                     }else{
4683                         if(args){
4684                             // quoted values are required for strings in compiled templates, 
4685                             // but for non compiled we need to strip them
4686                             // quoted reversed for jsmin
4687                             var re = /^\s*['"](.*)["']\s*$/;
4688                             args = args.split(',');
4689                             for(var i = 0, len = args.length; i < len; i++){
4690                                 args[i] = args[i].replace(re, "$1");
4691                             }
4692                             args = [values[name]].concat(args);
4693                         }else{
4694                             args = [values[name]];
4695                         }
4696                         return fm[format].apply(fm, args);
4697                     }
4698                 }else{
4699                     return values[name] !== undefined ? values[name] : "";
4700                 }
4701             };
4702             return this.html.replace(this.re, fn);
4703         } catch (e) {
4704             Roo.log(e);
4705             throw e;
4706         }
4707          
4708     },
4709     
4710     loading : false,
4711       
4712     load : function ()
4713     {
4714          
4715         if (this.loading) {
4716             return;
4717         }
4718         var _t = this;
4719         
4720         this.loading = true;
4721         this.compiled = false;
4722         
4723         var cx = new Roo.data.Connection();
4724         cx.request({
4725             url : this.url,
4726             method : 'GET',
4727             success : function (response) {
4728                 _t.loading = false;
4729                 _t.html = response.responseText;
4730                 _t.url = false;
4731                 _t.compile();
4732              },
4733             failure : function(response) {
4734                 Roo.log("Template failed to load from " + _t.url);
4735                 _t.loading = false;
4736             }
4737         });
4738     },
4739
4740     /**
4741      * Sets the HTML used as the template and optionally compiles it.
4742      * @param {String} html
4743      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4744      * @return {Roo.Template} this
4745      */
4746     set : function(html, compile){
4747         this.html = html;
4748         this.compiled = null;
4749         if(compile){
4750             this.compile();
4751         }
4752         return this;
4753     },
4754     
4755     /**
4756      * True to disable format functions (defaults to false)
4757      * @type Boolean
4758      */
4759     disableFormats : false,
4760     
4761     /**
4762     * The regular expression used to match template variables 
4763     * @type RegExp
4764     * @property 
4765     */
4766     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4767     
4768     /**
4769      * Compiles the template into an internal function, eliminating the RegEx overhead.
4770      * @return {Roo.Template} this
4771      */
4772     compile : function(){
4773         var fm = Roo.util.Format;
4774         var useF = this.disableFormats !== true;
4775         var sep = Roo.isGecko ? "+" : ",";
4776         var fn = function(m, name, format, args){
4777             if(format && useF){
4778                 args = args ? ',' + args : "";
4779                 if(format.substr(0, 5) != "this."){
4780                     format = "fm." + format + '(';
4781                 }else{
4782                     format = 'this.call("'+ format.substr(5) + '", ';
4783                     args = ", values";
4784                 }
4785             }else{
4786                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4787             }
4788             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4789         };
4790         var body;
4791         // branched to use + in gecko and [].join() in others
4792         if(Roo.isGecko){
4793             body = "this.compiled = function(values){ return '" +
4794                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4795                     "';};";
4796         }else{
4797             body = ["this.compiled = function(values){ return ['"];
4798             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4799             body.push("'].join('');};");
4800             body = body.join('');
4801         }
4802         /**
4803          * eval:var:values
4804          * eval:var:fm
4805          */
4806         eval(body);
4807         return this;
4808     },
4809     
4810     // private function used to call members
4811     call : function(fnName, value, allValues){
4812         return this[fnName](value, allValues);
4813     },
4814     
4815     /**
4816      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4817      * @param {String/HTMLElement/Roo.Element} el The context element
4818      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4819      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4820      * @return {HTMLElement/Roo.Element} The new node or Element
4821      */
4822     insertFirst: function(el, values, returnElement){
4823         return this.doInsert('afterBegin', el, values, returnElement);
4824     },
4825
4826     /**
4827      * Applies the supplied values to the template and inserts the new node(s) before el.
4828      * @param {String/HTMLElement/Roo.Element} el The context element
4829      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4830      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4831      * @return {HTMLElement/Roo.Element} The new node or Element
4832      */
4833     insertBefore: function(el, values, returnElement){
4834         return this.doInsert('beforeBegin', el, values, returnElement);
4835     },
4836
4837     /**
4838      * Applies the supplied values to the template and inserts the new node(s) after el.
4839      * @param {String/HTMLElement/Roo.Element} el The context element
4840      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4841      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4842      * @return {HTMLElement/Roo.Element} The new node or Element
4843      */
4844     insertAfter : function(el, values, returnElement){
4845         return this.doInsert('afterEnd', el, values, returnElement);
4846     },
4847     
4848     /**
4849      * Applies the supplied values to the template and appends the new node(s) to el.
4850      * @param {String/HTMLElement/Roo.Element} el The context element
4851      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4852      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4853      * @return {HTMLElement/Roo.Element} The new node or Element
4854      */
4855     append : function(el, values, returnElement){
4856         return this.doInsert('beforeEnd', el, values, returnElement);
4857     },
4858
4859     doInsert : function(where, el, values, returnEl){
4860         el = Roo.getDom(el);
4861         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4862         return returnEl ? Roo.get(newNode, true) : newNode;
4863     },
4864
4865     /**
4866      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4867      * @param {String/HTMLElement/Roo.Element} el The context element
4868      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4869      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4870      * @return {HTMLElement/Roo.Element} The new node or Element
4871      */
4872     overwrite : function(el, values, returnElement){
4873         el = Roo.getDom(el);
4874         el.innerHTML = this.applyTemplate(values);
4875         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4876     }
4877 };
4878 /**
4879  * Alias for {@link #applyTemplate}
4880  * @method
4881  */
4882 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4883
4884 // backwards compat
4885 Roo.DomHelper.Template = Roo.Template;
4886
4887 /**
4888  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4889  * @param {String/HTMLElement} el A DOM element or its id
4890  * @returns {Roo.Template} The created template
4891  * @static
4892  */
4893 Roo.Template.from = function(el){
4894     el = Roo.getDom(el);
4895     return new Roo.Template(el.value || el.innerHTML);
4896 };/*
4897  * Based on:
4898  * Ext JS Library 1.1.1
4899  * Copyright(c) 2006-2007, Ext JS, LLC.
4900  *
4901  * Originally Released Under LGPL - original licence link has changed is not relivant.
4902  *
4903  * Fork - LGPL
4904  * <script type="text/javascript">
4905  */
4906  
4907
4908 /*
4909  * This is code is also distributed under MIT license for use
4910  * with jQuery and prototype JavaScript libraries.
4911  */
4912 /**
4913  * @class Roo.DomQuery
4914 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4915 <p>
4916 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4917
4918 <p>
4919 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4920 </p>
4921 <h4>Element Selectors:</h4>
4922 <ul class="list">
4923     <li> <b>*</b> any element</li>
4924     <li> <b>E</b> an element with the tag E</li>
4925     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4926     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4927     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4928     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4929 </ul>
4930 <h4>Attribute Selectors:</h4>
4931 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4932 <ul class="list">
4933     <li> <b>E[foo]</b> has an attribute "foo"</li>
4934     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4935     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4936     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4937     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4938     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4939     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4940 </ul>
4941 <h4>Pseudo Classes:</h4>
4942 <ul class="list">
4943     <li> <b>E:first-child</b> E is the first child of its parent</li>
4944     <li> <b>E:last-child</b> E is the last child of its parent</li>
4945     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4946     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4947     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4948     <li> <b>E:only-child</b> E is the only child of its parent</li>
4949     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4950     <li> <b>E:first</b> the first E in the resultset</li>
4951     <li> <b>E:last</b> the last E in the resultset</li>
4952     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4953     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4954     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4955     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4956     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4957     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4958     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4959     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4960     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4961 </ul>
4962 <h4>CSS Value Selectors:</h4>
4963 <ul class="list">
4964     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4965     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4966     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4967     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4968     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4969     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4970 </ul>
4971  * @singleton
4972  */
4973 Roo.DomQuery = function(){
4974     var cache = {}, simpleCache = {}, valueCache = {};
4975     var nonSpace = /\S/;
4976     var trimRe = /^\s+|\s+$/g;
4977     var tplRe = /\{(\d+)\}/g;
4978     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4979     var tagTokenRe = /^(#)?([\w-\*]+)/;
4980     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4981
4982     function child(p, index){
4983         var i = 0;
4984         var n = p.firstChild;
4985         while(n){
4986             if(n.nodeType == 1){
4987                if(++i == index){
4988                    return n;
4989                }
4990             }
4991             n = n.nextSibling;
4992         }
4993         return null;
4994     };
4995
4996     function next(n){
4997         while((n = n.nextSibling) && n.nodeType != 1);
4998         return n;
4999     };
5000
5001     function prev(n){
5002         while((n = n.previousSibling) && n.nodeType != 1);
5003         return n;
5004     };
5005
5006     function children(d){
5007         var n = d.firstChild, ni = -1;
5008             while(n){
5009                 var nx = n.nextSibling;
5010                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5011                     d.removeChild(n);
5012                 }else{
5013                     n.nodeIndex = ++ni;
5014                 }
5015                 n = nx;
5016             }
5017             return this;
5018         };
5019
5020     function byClassName(c, a, v){
5021         if(!v){
5022             return c;
5023         }
5024         var r = [], ri = -1, cn;
5025         for(var i = 0, ci; ci = c[i]; i++){
5026             if((' '+ci.className+' ').indexOf(v) != -1){
5027                 r[++ri] = ci;
5028             }
5029         }
5030         return r;
5031     };
5032
5033     function attrValue(n, attr){
5034         if(!n.tagName && typeof n.length != "undefined"){
5035             n = n[0];
5036         }
5037         if(!n){
5038             return null;
5039         }
5040         if(attr == "for"){
5041             return n.htmlFor;
5042         }
5043         if(attr == "class" || attr == "className"){
5044             return n.className;
5045         }
5046         return n.getAttribute(attr) || n[attr];
5047
5048     };
5049
5050     function getNodes(ns, mode, tagName){
5051         var result = [], ri = -1, cs;
5052         if(!ns){
5053             return result;
5054         }
5055         tagName = tagName || "*";
5056         if(typeof ns.getElementsByTagName != "undefined"){
5057             ns = [ns];
5058         }
5059         if(!mode){
5060             for(var i = 0, ni; ni = ns[i]; i++){
5061                 cs = ni.getElementsByTagName(tagName);
5062                 for(var j = 0, ci; ci = cs[j]; j++){
5063                     result[++ri] = ci;
5064                 }
5065             }
5066         }else if(mode == "/" || mode == ">"){
5067             var utag = tagName.toUpperCase();
5068             for(var i = 0, ni, cn; ni = ns[i]; i++){
5069                 cn = ni.children || ni.childNodes;
5070                 for(var j = 0, cj; cj = cn[j]; j++){
5071                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5072                         result[++ri] = cj;
5073                     }
5074                 }
5075             }
5076         }else if(mode == "+"){
5077             var utag = tagName.toUpperCase();
5078             for(var i = 0, n; n = ns[i]; i++){
5079                 while((n = n.nextSibling) && n.nodeType != 1);
5080                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5081                     result[++ri] = n;
5082                 }
5083             }
5084         }else if(mode == "~"){
5085             for(var i = 0, n; n = ns[i]; i++){
5086                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5087                 if(n){
5088                     result[++ri] = n;
5089                 }
5090             }
5091         }
5092         return result;
5093     };
5094
5095     function concat(a, b){
5096         if(b.slice){
5097             return a.concat(b);
5098         }
5099         for(var i = 0, l = b.length; i < l; i++){
5100             a[a.length] = b[i];
5101         }
5102         return a;
5103     }
5104
5105     function byTag(cs, tagName){
5106         if(cs.tagName || cs == document){
5107             cs = [cs];
5108         }
5109         if(!tagName){
5110             return cs;
5111         }
5112         var r = [], ri = -1;
5113         tagName = tagName.toLowerCase();
5114         for(var i = 0, ci; ci = cs[i]; i++){
5115             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5116                 r[++ri] = ci;
5117             }
5118         }
5119         return r;
5120     };
5121
5122     function byId(cs, attr, id){
5123         if(cs.tagName || cs == document){
5124             cs = [cs];
5125         }
5126         if(!id){
5127             return cs;
5128         }
5129         var r = [], ri = -1;
5130         for(var i = 0,ci; ci = cs[i]; i++){
5131             if(ci && ci.id == id){
5132                 r[++ri] = ci;
5133                 return r;
5134             }
5135         }
5136         return r;
5137     };
5138
5139     function byAttribute(cs, attr, value, op, custom){
5140         var r = [], ri = -1, st = custom=="{";
5141         var f = Roo.DomQuery.operators[op];
5142         for(var i = 0, ci; ci = cs[i]; i++){
5143             var a;
5144             if(st){
5145                 a = Roo.DomQuery.getStyle(ci, attr);
5146             }
5147             else if(attr == "class" || attr == "className"){
5148                 a = ci.className;
5149             }else if(attr == "for"){
5150                 a = ci.htmlFor;
5151             }else if(attr == "href"){
5152                 a = ci.getAttribute("href", 2);
5153             }else{
5154                 a = ci.getAttribute(attr);
5155             }
5156             if((f && f(a, value)) || (!f && a)){
5157                 r[++ri] = ci;
5158             }
5159         }
5160         return r;
5161     };
5162
5163     function byPseudo(cs, name, value){
5164         return Roo.DomQuery.pseudos[name](cs, value);
5165     };
5166
5167     // This is for IE MSXML which does not support expandos.
5168     // IE runs the same speed using setAttribute, however FF slows way down
5169     // and Safari completely fails so they need to continue to use expandos.
5170     var isIE = window.ActiveXObject ? true : false;
5171
5172     // this eval is stop the compressor from
5173     // renaming the variable to something shorter
5174     
5175     /** eval:var:batch */
5176     var batch = 30803; 
5177
5178     var key = 30803;
5179
5180     function nodupIEXml(cs){
5181         var d = ++key;
5182         cs[0].setAttribute("_nodup", d);
5183         var r = [cs[0]];
5184         for(var i = 1, len = cs.length; i < len; i++){
5185             var c = cs[i];
5186             if(!c.getAttribute("_nodup") != d){
5187                 c.setAttribute("_nodup", d);
5188                 r[r.length] = c;
5189             }
5190         }
5191         for(var i = 0, len = cs.length; i < len; i++){
5192             cs[i].removeAttribute("_nodup");
5193         }
5194         return r;
5195     }
5196
5197     function nodup(cs){
5198         if(!cs){
5199             return [];
5200         }
5201         var len = cs.length, c, i, r = cs, cj, ri = -1;
5202         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5203             return cs;
5204         }
5205         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5206             return nodupIEXml(cs);
5207         }
5208         var d = ++key;
5209         cs[0]._nodup = d;
5210         for(i = 1; c = cs[i]; i++){
5211             if(c._nodup != d){
5212                 c._nodup = d;
5213             }else{
5214                 r = [];
5215                 for(var j = 0; j < i; j++){
5216                     r[++ri] = cs[j];
5217                 }
5218                 for(j = i+1; cj = cs[j]; j++){
5219                     if(cj._nodup != d){
5220                         cj._nodup = d;
5221                         r[++ri] = cj;
5222                     }
5223                 }
5224                 return r;
5225             }
5226         }
5227         return r;
5228     }
5229
5230     function quickDiffIEXml(c1, c2){
5231         var d = ++key;
5232         for(var i = 0, len = c1.length; i < len; i++){
5233             c1[i].setAttribute("_qdiff", d);
5234         }
5235         var r = [];
5236         for(var i = 0, len = c2.length; i < len; i++){
5237             if(c2[i].getAttribute("_qdiff") != d){
5238                 r[r.length] = c2[i];
5239             }
5240         }
5241         for(var i = 0, len = c1.length; i < len; i++){
5242            c1[i].removeAttribute("_qdiff");
5243         }
5244         return r;
5245     }
5246
5247     function quickDiff(c1, c2){
5248         var len1 = c1.length;
5249         if(!len1){
5250             return c2;
5251         }
5252         if(isIE && c1[0].selectSingleNode){
5253             return quickDiffIEXml(c1, c2);
5254         }
5255         var d = ++key;
5256         for(var i = 0; i < len1; i++){
5257             c1[i]._qdiff = d;
5258         }
5259         var r = [];
5260         for(var i = 0, len = c2.length; i < len; i++){
5261             if(c2[i]._qdiff != d){
5262                 r[r.length] = c2[i];
5263             }
5264         }
5265         return r;
5266     }
5267
5268     function quickId(ns, mode, root, id){
5269         if(ns == root){
5270            var d = root.ownerDocument || root;
5271            return d.getElementById(id);
5272         }
5273         ns = getNodes(ns, mode, "*");
5274         return byId(ns, null, id);
5275     }
5276
5277     return {
5278         getStyle : function(el, name){
5279             return Roo.fly(el).getStyle(name);
5280         },
5281         /**
5282          * Compiles a selector/xpath query into a reusable function. The returned function
5283          * takes one parameter "root" (optional), which is the context node from where the query should start.
5284          * @param {String} selector The selector/xpath query
5285          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5286          * @return {Function}
5287          */
5288         compile : function(path, type){
5289             type = type || "select";
5290             
5291             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5292             var q = path, mode, lq;
5293             var tk = Roo.DomQuery.matchers;
5294             var tklen = tk.length;
5295             var mm;
5296
5297             // accept leading mode switch
5298             var lmode = q.match(modeRe);
5299             if(lmode && lmode[1]){
5300                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5301                 q = q.replace(lmode[1], "");
5302             }
5303             // strip leading slashes
5304             while(path.substr(0, 1)=="/"){
5305                 path = path.substr(1);
5306             }
5307
5308             while(q && lq != q){
5309                 lq = q;
5310                 var tm = q.match(tagTokenRe);
5311                 if(type == "select"){
5312                     if(tm){
5313                         if(tm[1] == "#"){
5314                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5315                         }else{
5316                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5317                         }
5318                         q = q.replace(tm[0], "");
5319                     }else if(q.substr(0, 1) != '@'){
5320                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5321                     }
5322                 }else{
5323                     if(tm){
5324                         if(tm[1] == "#"){
5325                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5326                         }else{
5327                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5328                         }
5329                         q = q.replace(tm[0], "");
5330                     }
5331                 }
5332                 while(!(mm = q.match(modeRe))){
5333                     var matched = false;
5334                     for(var j = 0; j < tklen; j++){
5335                         var t = tk[j];
5336                         var m = q.match(t.re);
5337                         if(m){
5338                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5339                                                     return m[i];
5340                                                 });
5341                             q = q.replace(m[0], "");
5342                             matched = true;
5343                             break;
5344                         }
5345                     }
5346                     // prevent infinite loop on bad selector
5347                     if(!matched){
5348                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5349                     }
5350                 }
5351                 if(mm[1]){
5352                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5353                     q = q.replace(mm[1], "");
5354                 }
5355             }
5356             fn[fn.length] = "return nodup(n);\n}";
5357             
5358              /** 
5359               * list of variables that need from compression as they are used by eval.
5360              *  eval:var:batch 
5361              *  eval:var:nodup
5362              *  eval:var:byTag
5363              *  eval:var:ById
5364              *  eval:var:getNodes
5365              *  eval:var:quickId
5366              *  eval:var:mode
5367              *  eval:var:root
5368              *  eval:var:n
5369              *  eval:var:byClassName
5370              *  eval:var:byPseudo
5371              *  eval:var:byAttribute
5372              *  eval:var:attrValue
5373              * 
5374              **/ 
5375             eval(fn.join(""));
5376             return f;
5377         },
5378
5379         /**
5380          * Selects a group of elements.
5381          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5382          * @param {Node} root (optional) The start of the query (defaults to document).
5383          * @return {Array}
5384          */
5385         select : function(path, root, type){
5386             if(!root || root == document){
5387                 root = document;
5388             }
5389             if(typeof root == "string"){
5390                 root = document.getElementById(root);
5391             }
5392             var paths = path.split(",");
5393             var results = [];
5394             for(var i = 0, len = paths.length; i < len; i++){
5395                 var p = paths[i].replace(trimRe, "");
5396                 if(!cache[p]){
5397                     cache[p] = Roo.DomQuery.compile(p);
5398                     if(!cache[p]){
5399                         throw p + " is not a valid selector";
5400                     }
5401                 }
5402                 var result = cache[p](root);
5403                 if(result && result != document){
5404                     results = results.concat(result);
5405                 }
5406             }
5407             if(paths.length > 1){
5408                 return nodup(results);
5409             }
5410             return results;
5411         },
5412
5413         /**
5414          * Selects a single element.
5415          * @param {String} selector The selector/xpath query
5416          * @param {Node} root (optional) The start of the query (defaults to document).
5417          * @return {Element}
5418          */
5419         selectNode : function(path, root){
5420             return Roo.DomQuery.select(path, root)[0];
5421         },
5422
5423         /**
5424          * Selects the value of a node, optionally replacing null with the defaultValue.
5425          * @param {String} selector The selector/xpath query
5426          * @param {Node} root (optional) The start of the query (defaults to document).
5427          * @param {String} defaultValue
5428          */
5429         selectValue : function(path, root, defaultValue){
5430             path = path.replace(trimRe, "");
5431             if(!valueCache[path]){
5432                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5433             }
5434             var n = valueCache[path](root);
5435             n = n[0] ? n[0] : n;
5436             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5437             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5438         },
5439
5440         /**
5441          * Selects the value of a node, parsing integers and floats.
5442          * @param {String} selector The selector/xpath query
5443          * @param {Node} root (optional) The start of the query (defaults to document).
5444          * @param {Number} defaultValue
5445          * @return {Number}
5446          */
5447         selectNumber : function(path, root, defaultValue){
5448             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5449             return parseFloat(v);
5450         },
5451
5452         /**
5453          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5454          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5455          * @param {String} selector The simple selector to test
5456          * @return {Boolean}
5457          */
5458         is : function(el, ss){
5459             if(typeof el == "string"){
5460                 el = document.getElementById(el);
5461             }
5462             var isArray = (el instanceof Array);
5463             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5464             return isArray ? (result.length == el.length) : (result.length > 0);
5465         },
5466
5467         /**
5468          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5469          * @param {Array} el An array of elements to filter
5470          * @param {String} selector The simple selector to test
5471          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5472          * the selector instead of the ones that match
5473          * @return {Array}
5474          */
5475         filter : function(els, ss, nonMatches){
5476             ss = ss.replace(trimRe, "");
5477             if(!simpleCache[ss]){
5478                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5479             }
5480             var result = simpleCache[ss](els);
5481             return nonMatches ? quickDiff(result, els) : result;
5482         },
5483
5484         /**
5485          * Collection of matching regular expressions and code snippets.
5486          */
5487         matchers : [{
5488                 re: /^\.([\w-]+)/,
5489                 select: 'n = byClassName(n, null, " {1} ");'
5490             }, {
5491                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5492                 select: 'n = byPseudo(n, "{1}", "{2}");'
5493             },{
5494                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5495                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5496             }, {
5497                 re: /^#([\w-]+)/,
5498                 select: 'n = byId(n, null, "{1}");'
5499             },{
5500                 re: /^@([\w-]+)/,
5501                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5502             }
5503         ],
5504
5505         /**
5506          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5507          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5508          */
5509         operators : {
5510             "=" : function(a, v){
5511                 return a == v;
5512             },
5513             "!=" : function(a, v){
5514                 return a != v;
5515             },
5516             "^=" : function(a, v){
5517                 return a && a.substr(0, v.length) == v;
5518             },
5519             "$=" : function(a, v){
5520                 return a && a.substr(a.length-v.length) == v;
5521             },
5522             "*=" : function(a, v){
5523                 return a && a.indexOf(v) !== -1;
5524             },
5525             "%=" : function(a, v){
5526                 return (a % v) == 0;
5527             },
5528             "|=" : function(a, v){
5529                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5530             },
5531             "~=" : function(a, v){
5532                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5533             }
5534         },
5535
5536         /**
5537          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5538          * and the argument (if any) supplied in the selector.
5539          */
5540         pseudos : {
5541             "first-child" : function(c){
5542                 var r = [], ri = -1, n;
5543                 for(var i = 0, ci; ci = n = c[i]; i++){
5544                     while((n = n.previousSibling) && n.nodeType != 1);
5545                     if(!n){
5546                         r[++ri] = ci;
5547                     }
5548                 }
5549                 return r;
5550             },
5551
5552             "last-child" : function(c){
5553                 var r = [], ri = -1, n;
5554                 for(var i = 0, ci; ci = n = c[i]; i++){
5555                     while((n = n.nextSibling) && n.nodeType != 1);
5556                     if(!n){
5557                         r[++ri] = ci;
5558                     }
5559                 }
5560                 return r;
5561             },
5562
5563             "nth-child" : function(c, a) {
5564                 var r = [], ri = -1;
5565                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5566                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5567                 for(var i = 0, n; n = c[i]; i++){
5568                     var pn = n.parentNode;
5569                     if (batch != pn._batch) {
5570                         var j = 0;
5571                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5572                             if(cn.nodeType == 1){
5573                                cn.nodeIndex = ++j;
5574                             }
5575                         }
5576                         pn._batch = batch;
5577                     }
5578                     if (f == 1) {
5579                         if (l == 0 || n.nodeIndex == l){
5580                             r[++ri] = n;
5581                         }
5582                     } else if ((n.nodeIndex + l) % f == 0){
5583                         r[++ri] = n;
5584                     }
5585                 }
5586
5587                 return r;
5588             },
5589
5590             "only-child" : function(c){
5591                 var r = [], ri = -1;;
5592                 for(var i = 0, ci; ci = c[i]; i++){
5593                     if(!prev(ci) && !next(ci)){
5594                         r[++ri] = ci;
5595                     }
5596                 }
5597                 return r;
5598             },
5599
5600             "empty" : function(c){
5601                 var r = [], ri = -1;
5602                 for(var i = 0, ci; ci = c[i]; i++){
5603                     var cns = ci.childNodes, j = 0, cn, empty = true;
5604                     while(cn = cns[j]){
5605                         ++j;
5606                         if(cn.nodeType == 1 || cn.nodeType == 3){
5607                             empty = false;
5608                             break;
5609                         }
5610                     }
5611                     if(empty){
5612                         r[++ri] = ci;
5613                     }
5614                 }
5615                 return r;
5616             },
5617
5618             "contains" : function(c, v){
5619                 var r = [], ri = -1;
5620                 for(var i = 0, ci; ci = c[i]; i++){
5621                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5622                         r[++ri] = ci;
5623                     }
5624                 }
5625                 return r;
5626             },
5627
5628             "nodeValue" : function(c, v){
5629                 var r = [], ri = -1;
5630                 for(var i = 0, ci; ci = c[i]; i++){
5631                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5632                         r[++ri] = ci;
5633                     }
5634                 }
5635                 return r;
5636             },
5637
5638             "checked" : function(c){
5639                 var r = [], ri = -1;
5640                 for(var i = 0, ci; ci = c[i]; i++){
5641                     if(ci.checked == true){
5642                         r[++ri] = ci;
5643                     }
5644                 }
5645                 return r;
5646             },
5647
5648             "not" : function(c, ss){
5649                 return Roo.DomQuery.filter(c, ss, true);
5650             },
5651
5652             "odd" : function(c){
5653                 return this["nth-child"](c, "odd");
5654             },
5655
5656             "even" : function(c){
5657                 return this["nth-child"](c, "even");
5658             },
5659
5660             "nth" : function(c, a){
5661                 return c[a-1] || [];
5662             },
5663
5664             "first" : function(c){
5665                 return c[0] || [];
5666             },
5667
5668             "last" : function(c){
5669                 return c[c.length-1] || [];
5670             },
5671
5672             "has" : function(c, ss){
5673                 var s = Roo.DomQuery.select;
5674                 var r = [], ri = -1;
5675                 for(var i = 0, ci; ci = c[i]; i++){
5676                     if(s(ss, ci).length > 0){
5677                         r[++ri] = ci;
5678                     }
5679                 }
5680                 return r;
5681             },
5682
5683             "next" : function(c, ss){
5684                 var is = Roo.DomQuery.is;
5685                 var r = [], ri = -1;
5686                 for(var i = 0, ci; ci = c[i]; i++){
5687                     var n = next(ci);
5688                     if(n && is(n, ss)){
5689                         r[++ri] = ci;
5690                     }
5691                 }
5692                 return r;
5693             },
5694
5695             "prev" : function(c, ss){
5696                 var is = Roo.DomQuery.is;
5697                 var r = [], ri = -1;
5698                 for(var i = 0, ci; ci = c[i]; i++){
5699                     var n = prev(ci);
5700                     if(n && is(n, ss)){
5701                         r[++ri] = ci;
5702                     }
5703                 }
5704                 return r;
5705             }
5706         }
5707     };
5708 }();
5709
5710 /**
5711  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5712  * @param {String} path The selector/xpath query
5713  * @param {Node} root (optional) The start of the query (defaults to document).
5714  * @return {Array}
5715  * @member Roo
5716  * @method query
5717  */
5718 Roo.query = Roo.DomQuery.select;
5719 /*
5720  * Based on:
5721  * Ext JS Library 1.1.1
5722  * Copyright(c) 2006-2007, Ext JS, LLC.
5723  *
5724  * Originally Released Under LGPL - original licence link has changed is not relivant.
5725  *
5726  * Fork - LGPL
5727  * <script type="text/javascript">
5728  */
5729
5730 /**
5731  * @class Roo.util.Observable
5732  * Base class that provides a common interface for publishing events. Subclasses are expected to
5733  * to have a property "events" with all the events defined.<br>
5734  * For example:
5735  * <pre><code>
5736  Employee = function(name){
5737     this.name = name;
5738     this.addEvents({
5739         "fired" : true,
5740         "quit" : true
5741     });
5742  }
5743  Roo.extend(Employee, Roo.util.Observable);
5744 </code></pre>
5745  * @param {Object} config properties to use (incuding events / listeners)
5746  */
5747
5748 Roo.util.Observable = function(cfg){
5749     
5750     cfg = cfg|| {};
5751     this.addEvents(cfg.events || {});
5752     if (cfg.events) {
5753         delete cfg.events; // make sure
5754     }
5755      
5756     Roo.apply(this, cfg);
5757     
5758     if(this.listeners){
5759         this.on(this.listeners);
5760         delete this.listeners;
5761     }
5762 };
5763 Roo.util.Observable.prototype = {
5764     /** 
5765  * @cfg {Object} listeners  list of events and functions to call for this object, 
5766  * For example :
5767  * <pre><code>
5768     listeners :  { 
5769        'click' : function(e) {
5770            ..... 
5771         } ,
5772         .... 
5773     } 
5774   </code></pre>
5775  */
5776     
5777     
5778     /**
5779      * Fires the specified event with the passed parameters (minus the event name).
5780      * @param {String} eventName
5781      * @param {Object...} args Variable number of parameters are passed to handlers
5782      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5783      */
5784     fireEvent : function(){
5785         var ce = this.events[arguments[0].toLowerCase()];
5786         if(typeof ce == "object"){
5787             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5788         }else{
5789             return true;
5790         }
5791     },
5792
5793     // private
5794     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5795
5796     /**
5797      * Appends an event handler to this component
5798      * @param {String}   eventName The type of event to listen for
5799      * @param {Function} handler The method the event invokes
5800      * @param {Object}   scope (optional) The scope in which to execute the handler
5801      * function. The handler function's "this" context.
5802      * @param {Object}   options (optional) An object containing handler configuration
5803      * properties. This may contain any of the following properties:<ul>
5804      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5805      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5806      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5807      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5808      * by the specified number of milliseconds. If the event fires again within that time, the original
5809      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5810      * </ul><br>
5811      * <p>
5812      * <b>Combining Options</b><br>
5813      * Using the options argument, it is possible to combine different types of listeners:<br>
5814      * <br>
5815      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5816                 <pre><code>
5817                 el.on('click', this.onClick, this, {
5818                         single: true,
5819                 delay: 100,
5820                 forumId: 4
5821                 });
5822                 </code></pre>
5823      * <p>
5824      * <b>Attaching multiple handlers in 1 call</b><br>
5825      * The method also allows for a single argument to be passed which is a config object containing properties
5826      * which specify multiple handlers.
5827      * <pre><code>
5828                 el.on({
5829                         'click': {
5830                         fn: this.onClick,
5831                         scope: this,
5832                         delay: 100
5833                 }, 
5834                 'mouseover': {
5835                         fn: this.onMouseOver,
5836                         scope: this
5837                 },
5838                 'mouseout': {
5839                         fn: this.onMouseOut,
5840                         scope: this
5841                 }
5842                 });
5843                 </code></pre>
5844      * <p>
5845      * Or a shorthand syntax which passes the same scope object to all handlers:
5846         <pre><code>
5847                 el.on({
5848                         'click': this.onClick,
5849                 'mouseover': this.onMouseOver,
5850                 'mouseout': this.onMouseOut,
5851                 scope: this
5852                 });
5853                 </code></pre>
5854      */
5855     addListener : function(eventName, fn, scope, o){
5856         if(typeof eventName == "object"){
5857             o = eventName;
5858             for(var e in o){
5859                 if(this.filterOptRe.test(e)){
5860                     continue;
5861                 }
5862                 if(typeof o[e] == "function"){
5863                     // shared options
5864                     this.addListener(e, o[e], o.scope,  o);
5865                 }else{
5866                     // individual options
5867                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5868                 }
5869             }
5870             return;
5871         }
5872         o = (!o || typeof o == "boolean") ? {} : o;
5873         eventName = eventName.toLowerCase();
5874         var ce = this.events[eventName] || true;
5875         if(typeof ce == "boolean"){
5876             ce = new Roo.util.Event(this, eventName);
5877             this.events[eventName] = ce;
5878         }
5879         ce.addListener(fn, scope, o);
5880     },
5881
5882     /**
5883      * Removes a listener
5884      * @param {String}   eventName     The type of event to listen for
5885      * @param {Function} handler        The handler to remove
5886      * @param {Object}   scope  (optional) The scope (this object) for the handler
5887      */
5888     removeListener : function(eventName, fn, scope){
5889         var ce = this.events[eventName.toLowerCase()];
5890         if(typeof ce == "object"){
5891             ce.removeListener(fn, scope);
5892         }
5893     },
5894
5895     /**
5896      * Removes all listeners for this object
5897      */
5898     purgeListeners : function(){
5899         for(var evt in this.events){
5900             if(typeof this.events[evt] == "object"){
5901                  this.events[evt].clearListeners();
5902             }
5903         }
5904     },
5905
5906     relayEvents : function(o, events){
5907         var createHandler = function(ename){
5908             return function(){
5909                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5910             };
5911         };
5912         for(var i = 0, len = events.length; i < len; i++){
5913             var ename = events[i];
5914             if(!this.events[ename]){ this.events[ename] = true; };
5915             o.on(ename, createHandler(ename), this);
5916         }
5917     },
5918
5919     /**
5920      * Used to define events on this Observable
5921      * @param {Object} object The object with the events defined
5922      */
5923     addEvents : function(o){
5924         if(!this.events){
5925             this.events = {};
5926         }
5927         Roo.applyIf(this.events, o);
5928     },
5929
5930     /**
5931      * Checks to see if this object has any listeners for a specified event
5932      * @param {String} eventName The name of the event to check for
5933      * @return {Boolean} True if the event is being listened for, else false
5934      */
5935     hasListener : function(eventName){
5936         var e = this.events[eventName];
5937         return typeof e == "object" && e.listeners.length > 0;
5938     }
5939 };
5940 /**
5941  * Appends an event handler to this element (shorthand for addListener)
5942  * @param {String}   eventName     The type of event to listen for
5943  * @param {Function} handler        The method the event invokes
5944  * @param {Object}   scope (optional) The scope in which to execute the handler
5945  * function. The handler function's "this" context.
5946  * @param {Object}   options  (optional)
5947  * @method
5948  */
5949 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5950 /**
5951  * Removes a listener (shorthand for removeListener)
5952  * @param {String}   eventName     The type of event to listen for
5953  * @param {Function} handler        The handler to remove
5954  * @param {Object}   scope  (optional) The scope (this object) for the handler
5955  * @method
5956  */
5957 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5958
5959 /**
5960  * Starts capture on the specified Observable. All events will be passed
5961  * to the supplied function with the event name + standard signature of the event
5962  * <b>before</b> the event is fired. If the supplied function returns false,
5963  * the event will not fire.
5964  * @param {Observable} o The Observable to capture
5965  * @param {Function} fn The function to call
5966  * @param {Object} scope (optional) The scope (this object) for the fn
5967  * @static
5968  */
5969 Roo.util.Observable.capture = function(o, fn, scope){
5970     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5971 };
5972
5973 /**
5974  * Removes <b>all</b> added captures from the Observable.
5975  * @param {Observable} o The Observable to release
5976  * @static
5977  */
5978 Roo.util.Observable.releaseCapture = function(o){
5979     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5980 };
5981
5982 (function(){
5983
5984     var createBuffered = function(h, o, scope){
5985         var task = new Roo.util.DelayedTask();
5986         return function(){
5987             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5988         };
5989     };
5990
5991     var createSingle = function(h, e, fn, scope){
5992         return function(){
5993             e.removeListener(fn, scope);
5994             return h.apply(scope, arguments);
5995         };
5996     };
5997
5998     var createDelayed = function(h, o, scope){
5999         return function(){
6000             var args = Array.prototype.slice.call(arguments, 0);
6001             setTimeout(function(){
6002                 h.apply(scope, args);
6003             }, o.delay || 10);
6004         };
6005     };
6006
6007     Roo.util.Event = function(obj, name){
6008         this.name = name;
6009         this.obj = obj;
6010         this.listeners = [];
6011     };
6012
6013     Roo.util.Event.prototype = {
6014         addListener : function(fn, scope, options){
6015             var o = options || {};
6016             scope = scope || this.obj;
6017             if(!this.isListening(fn, scope)){
6018                 var l = {fn: fn, scope: scope, options: o};
6019                 var h = fn;
6020                 if(o.delay){
6021                     h = createDelayed(h, o, scope);
6022                 }
6023                 if(o.single){
6024                     h = createSingle(h, this, fn, scope);
6025                 }
6026                 if(o.buffer){
6027                     h = createBuffered(h, o, scope);
6028                 }
6029                 l.fireFn = h;
6030                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6031                     this.listeners.push(l);
6032                 }else{
6033                     this.listeners = this.listeners.slice(0);
6034                     this.listeners.push(l);
6035                 }
6036             }
6037         },
6038
6039         findListener : function(fn, scope){
6040             scope = scope || this.obj;
6041             var ls = this.listeners;
6042             for(var i = 0, len = ls.length; i < len; i++){
6043                 var l = ls[i];
6044                 if(l.fn == fn && l.scope == scope){
6045                     return i;
6046                 }
6047             }
6048             return -1;
6049         },
6050
6051         isListening : function(fn, scope){
6052             return this.findListener(fn, scope) != -1;
6053         },
6054
6055         removeListener : function(fn, scope){
6056             var index;
6057             if((index = this.findListener(fn, scope)) != -1){
6058                 if(!this.firing){
6059                     this.listeners.splice(index, 1);
6060                 }else{
6061                     this.listeners = this.listeners.slice(0);
6062                     this.listeners.splice(index, 1);
6063                 }
6064                 return true;
6065             }
6066             return false;
6067         },
6068
6069         clearListeners : function(){
6070             this.listeners = [];
6071         },
6072
6073         fire : function(){
6074             var ls = this.listeners, scope, len = ls.length;
6075             if(len > 0){
6076                 this.firing = true;
6077                 
6078                 for(var i = 0; i < len; i++){
6079                     var args = Array.prototype.slice.call(arguments, 0);
6080                     var l = ls[i];
6081                     args.push(l.options);
6082                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6083                         this.firing = false;
6084                         return false;
6085                     }
6086                 }
6087                 this.firing = false;
6088             }
6089             return true;
6090         }
6091     };
6092 })();/*
6093  * RooJS Library 
6094  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6095  *
6096  * Licence LGPL 
6097  *
6098  */
6099  
6100 /**
6101  * @class Roo.Document
6102  * @extends Roo.util.Observable
6103  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6104  * 
6105  * @param {Object} config the methods and properties of the 'base' class for the application.
6106  * 
6107  *  Generic Page handler - implement this to start your app..
6108  * 
6109  * eg.
6110  *  MyProject = new Roo.Document({
6111         events : {
6112             'load' : true // your events..
6113         },
6114         listeners : {
6115             'ready' : function() {
6116                 // fired on Roo.onReady()
6117             }
6118         }
6119  * 
6120  */
6121 Roo.Document = function(cfg) {
6122      
6123     this.addEvents({ 
6124         'ready' : true
6125     });
6126     Roo.util.Observable.call(this,cfg);
6127     
6128     var _this = this;
6129     
6130     Roo.onReady(function() {
6131         _this.fireEvent('ready');
6132     },null,false);
6133     
6134     
6135 }
6136
6137 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6138  * Based on:
6139  * Ext JS Library 1.1.1
6140  * Copyright(c) 2006-2007, Ext JS, LLC.
6141  *
6142  * Originally Released Under LGPL - original licence link has changed is not relivant.
6143  *
6144  * Fork - LGPL
6145  * <script type="text/javascript">
6146  */
6147
6148 /**
6149  * @class Roo.EventManager
6150  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6151  * several useful events directly.
6152  * See {@link Roo.EventObject} for more details on normalized event objects.
6153  * @singleton
6154  */
6155 Roo.EventManager = function(){
6156     var docReadyEvent, docReadyProcId, docReadyState = false;
6157     var resizeEvent, resizeTask, textEvent, textSize;
6158     var E = Roo.lib.Event;
6159     var D = Roo.lib.Dom;
6160
6161     
6162     
6163
6164     var fireDocReady = function(){
6165         if(!docReadyState){
6166             docReadyState = true;
6167             Roo.isReady = true;
6168             if(docReadyProcId){
6169                 clearInterval(docReadyProcId);
6170             }
6171             if(Roo.isGecko || Roo.isOpera) {
6172                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6173             }
6174             if(Roo.isIE){
6175                 var defer = document.getElementById("ie-deferred-loader");
6176                 if(defer){
6177                     defer.onreadystatechange = null;
6178                     defer.parentNode.removeChild(defer);
6179                 }
6180             }
6181             if(docReadyEvent){
6182                 docReadyEvent.fire();
6183                 docReadyEvent.clearListeners();
6184             }
6185         }
6186     };
6187     
6188     var initDocReady = function(){
6189         docReadyEvent = new Roo.util.Event();
6190         if(Roo.isGecko || Roo.isOpera) {
6191             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6192         }else if(Roo.isIE){
6193             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6194             var defer = document.getElementById("ie-deferred-loader");
6195             defer.onreadystatechange = function(){
6196                 if(this.readyState == "complete"){
6197                     fireDocReady();
6198                 }
6199             };
6200         }else if(Roo.isSafari){ 
6201             docReadyProcId = setInterval(function(){
6202                 var rs = document.readyState;
6203                 if(rs == "complete") {
6204                     fireDocReady();     
6205                  }
6206             }, 10);
6207         }
6208         // no matter what, make sure it fires on load
6209         E.on(window, "load", fireDocReady);
6210     };
6211
6212     var createBuffered = function(h, o){
6213         var task = new Roo.util.DelayedTask(h);
6214         return function(e){
6215             // create new event object impl so new events don't wipe out properties
6216             e = new Roo.EventObjectImpl(e);
6217             task.delay(o.buffer, h, null, [e]);
6218         };
6219     };
6220
6221     var createSingle = function(h, el, ename, fn){
6222         return function(e){
6223             Roo.EventManager.removeListener(el, ename, fn);
6224             h(e);
6225         };
6226     };
6227
6228     var createDelayed = function(h, o){
6229         return function(e){
6230             // create new event object impl so new events don't wipe out properties
6231             e = new Roo.EventObjectImpl(e);
6232             setTimeout(function(){
6233                 h(e);
6234             }, o.delay || 10);
6235         };
6236     };
6237     var transitionEndVal = false;
6238     
6239     var transitionEnd = function()
6240     {
6241         if (transitionEndVal) {
6242             return transitionEndVal;
6243         }
6244         var el = document.createElement('div');
6245
6246         var transEndEventNames = {
6247             WebkitTransition : 'webkitTransitionEnd',
6248             MozTransition    : 'transitionend',
6249             OTransition      : 'oTransitionEnd otransitionend',
6250             transition       : 'transitionend'
6251         };
6252     
6253         for (var name in transEndEventNames) {
6254             if (el.style[name] !== undefined) {
6255                 transitionEndVal = transEndEventNames[name];
6256                 return  transitionEndVal ;
6257             }
6258         }
6259     }
6260     
6261
6262     var listen = function(element, ename, opt, fn, scope){
6263         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6264         fn = fn || o.fn; scope = scope || o.scope;
6265         var el = Roo.getDom(element);
6266         
6267         
6268         if(!el){
6269             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6270         }
6271         
6272         if (ename == 'transitionend') {
6273             ename = transitionEnd();
6274         }
6275         var h = function(e){
6276             e = Roo.EventObject.setEvent(e);
6277             var t;
6278             if(o.delegate){
6279                 t = e.getTarget(o.delegate, el);
6280                 if(!t){
6281                     return;
6282                 }
6283             }else{
6284                 t = e.target;
6285             }
6286             if(o.stopEvent === true){
6287                 e.stopEvent();
6288             }
6289             if(o.preventDefault === true){
6290                e.preventDefault();
6291             }
6292             if(o.stopPropagation === true){
6293                 e.stopPropagation();
6294             }
6295
6296             if(o.normalized === false){
6297                 e = e.browserEvent;
6298             }
6299
6300             fn.call(scope || el, e, t, o);
6301         };
6302         if(o.delay){
6303             h = createDelayed(h, o);
6304         }
6305         if(o.single){
6306             h = createSingle(h, el, ename, fn);
6307         }
6308         if(o.buffer){
6309             h = createBuffered(h, o);
6310         }
6311         
6312         fn._handlers = fn._handlers || [];
6313         
6314         
6315         fn._handlers.push([Roo.id(el), ename, h]);
6316         
6317         
6318          
6319         E.on(el, ename, h);
6320         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6321             el.addEventListener("DOMMouseScroll", h, false);
6322             E.on(window, 'unload', function(){
6323                 el.removeEventListener("DOMMouseScroll", h, false);
6324             });
6325         }
6326         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6327             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6328         }
6329         return h;
6330     };
6331
6332     var stopListening = function(el, ename, fn){
6333         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6334         if(hds){
6335             for(var i = 0, len = hds.length; i < len; i++){
6336                 var h = hds[i];
6337                 if(h[0] == id && h[1] == ename){
6338                     hd = h[2];
6339                     hds.splice(i, 1);
6340                     break;
6341                 }
6342             }
6343         }
6344         E.un(el, ename, hd);
6345         el = Roo.getDom(el);
6346         if(ename == "mousewheel" && el.addEventListener){
6347             el.removeEventListener("DOMMouseScroll", hd, false);
6348         }
6349         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6350             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6351         }
6352     };
6353
6354     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6355     
6356     var pub = {
6357         
6358         
6359         /** 
6360          * Fix for doc tools
6361          * @scope Roo.EventManager
6362          */
6363         
6364         
6365         /** 
6366          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6367          * object with a Roo.EventObject
6368          * @param {Function} fn        The method the event invokes
6369          * @param {Object}   scope    An object that becomes the scope of the handler
6370          * @param {boolean}  override If true, the obj passed in becomes
6371          *                             the execution scope of the listener
6372          * @return {Function} The wrapped function
6373          * @deprecated
6374          */
6375         wrap : function(fn, scope, override){
6376             return function(e){
6377                 Roo.EventObject.setEvent(e);
6378                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6379             };
6380         },
6381         
6382         /**
6383      * Appends an event handler to an element (shorthand for addListener)
6384      * @param {String/HTMLElement}   element        The html element or id to assign the
6385      * @param {String}   eventName The type of event to listen for
6386      * @param {Function} handler The method the event invokes
6387      * @param {Object}   scope (optional) The scope in which to execute the handler
6388      * function. The handler function's "this" context.
6389      * @param {Object}   options (optional) An object containing handler configuration
6390      * properties. This may contain any of the following properties:<ul>
6391      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6392      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6393      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6394      * <li>preventDefault {Boolean} True to prevent the default action</li>
6395      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6396      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6397      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6398      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6399      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6400      * by the specified number of milliseconds. If the event fires again within that time, the original
6401      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6402      * </ul><br>
6403      * <p>
6404      * <b>Combining Options</b><br>
6405      * Using the options argument, it is possible to combine different types of listeners:<br>
6406      * <br>
6407      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6408      * Code:<pre><code>
6409 el.on('click', this.onClick, this, {
6410     single: true,
6411     delay: 100,
6412     stopEvent : true,
6413     forumId: 4
6414 });</code></pre>
6415      * <p>
6416      * <b>Attaching multiple handlers in 1 call</b><br>
6417       * The method also allows for a single argument to be passed which is a config object containing properties
6418      * which specify multiple handlers.
6419      * <p>
6420      * Code:<pre><code>
6421 el.on({
6422     'click' : {
6423         fn: this.onClick
6424         scope: this,
6425         delay: 100
6426     },
6427     'mouseover' : {
6428         fn: this.onMouseOver
6429         scope: this
6430     },
6431     'mouseout' : {
6432         fn: this.onMouseOut
6433         scope: this
6434     }
6435 });</code></pre>
6436      * <p>
6437      * Or a shorthand syntax:<br>
6438      * Code:<pre><code>
6439 el.on({
6440     'click' : this.onClick,
6441     'mouseover' : this.onMouseOver,
6442     'mouseout' : this.onMouseOut
6443     scope: this
6444 });</code></pre>
6445      */
6446         addListener : function(element, eventName, fn, scope, options){
6447             if(typeof eventName == "object"){
6448                 var o = eventName;
6449                 for(var e in o){
6450                     if(propRe.test(e)){
6451                         continue;
6452                     }
6453                     if(typeof o[e] == "function"){
6454                         // shared options
6455                         listen(element, e, o, o[e], o.scope);
6456                     }else{
6457                         // individual options
6458                         listen(element, e, o[e]);
6459                     }
6460                 }
6461                 return;
6462             }
6463             return listen(element, eventName, options, fn, scope);
6464         },
6465         
6466         /**
6467          * Removes an event handler
6468          *
6469          * @param {String/HTMLElement}   element        The id or html element to remove the 
6470          *                             event from
6471          * @param {String}   eventName     The type of event
6472          * @param {Function} fn
6473          * @return {Boolean} True if a listener was actually removed
6474          */
6475         removeListener : function(element, eventName, fn){
6476             return stopListening(element, eventName, fn);
6477         },
6478         
6479         /**
6480          * Fires when the document is ready (before onload and before images are loaded). Can be 
6481          * accessed shorthanded Roo.onReady().
6482          * @param {Function} fn        The method the event invokes
6483          * @param {Object}   scope    An  object that becomes the scope of the handler
6484          * @param {boolean}  options
6485          */
6486         onDocumentReady : function(fn, scope, options){
6487             if(docReadyState){ // if it already fired
6488                 docReadyEvent.addListener(fn, scope, options);
6489                 docReadyEvent.fire();
6490                 docReadyEvent.clearListeners();
6491                 return;
6492             }
6493             if(!docReadyEvent){
6494                 initDocReady();
6495             }
6496             docReadyEvent.addListener(fn, scope, options);
6497         },
6498         
6499         /**
6500          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6501          * @param {Function} fn        The method the event invokes
6502          * @param {Object}   scope    An object that becomes the scope of the handler
6503          * @param {boolean}  options
6504          */
6505         onWindowResize : function(fn, scope, options){
6506             if(!resizeEvent){
6507                 resizeEvent = new Roo.util.Event();
6508                 resizeTask = new Roo.util.DelayedTask(function(){
6509                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6510                 });
6511                 E.on(window, "resize", function(){
6512                     if(Roo.isIE){
6513                         resizeTask.delay(50);
6514                     }else{
6515                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6516                     }
6517                 });
6518             }
6519             resizeEvent.addListener(fn, scope, options);
6520         },
6521
6522         /**
6523          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6524          * @param {Function} fn        The method the event invokes
6525          * @param {Object}   scope    An object that becomes the scope of the handler
6526          * @param {boolean}  options
6527          */
6528         onTextResize : function(fn, scope, options){
6529             if(!textEvent){
6530                 textEvent = new Roo.util.Event();
6531                 var textEl = new Roo.Element(document.createElement('div'));
6532                 textEl.dom.className = 'x-text-resize';
6533                 textEl.dom.innerHTML = 'X';
6534                 textEl.appendTo(document.body);
6535                 textSize = textEl.dom.offsetHeight;
6536                 setInterval(function(){
6537                     if(textEl.dom.offsetHeight != textSize){
6538                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6539                     }
6540                 }, this.textResizeInterval);
6541             }
6542             textEvent.addListener(fn, scope, options);
6543         },
6544
6545         /**
6546          * Removes the passed window resize listener.
6547          * @param {Function} fn        The method the event invokes
6548          * @param {Object}   scope    The scope of handler
6549          */
6550         removeResizeListener : function(fn, scope){
6551             if(resizeEvent){
6552                 resizeEvent.removeListener(fn, scope);
6553             }
6554         },
6555
6556         // private
6557         fireResize : function(){
6558             if(resizeEvent){
6559                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6560             }   
6561         },
6562         /**
6563          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6564          */
6565         ieDeferSrc : false,
6566         /**
6567          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6568          */
6569         textResizeInterval : 50
6570     };
6571     
6572     /**
6573      * Fix for doc tools
6574      * @scopeAlias pub=Roo.EventManager
6575      */
6576     
6577      /**
6578      * Appends an event handler to an element (shorthand for addListener)
6579      * @param {String/HTMLElement}   element        The html element or id to assign the
6580      * @param {String}   eventName The type of event to listen for
6581      * @param {Function} handler The method the event invokes
6582      * @param {Object}   scope (optional) The scope in which to execute the handler
6583      * function. The handler function's "this" context.
6584      * @param {Object}   options (optional) An object containing handler configuration
6585      * properties. This may contain any of the following properties:<ul>
6586      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6587      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6588      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6589      * <li>preventDefault {Boolean} True to prevent the default action</li>
6590      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6591      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6592      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6593      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6594      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6595      * by the specified number of milliseconds. If the event fires again within that time, the original
6596      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6597      * </ul><br>
6598      * <p>
6599      * <b>Combining Options</b><br>
6600      * Using the options argument, it is possible to combine different types of listeners:<br>
6601      * <br>
6602      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6603      * Code:<pre><code>
6604 el.on('click', this.onClick, this, {
6605     single: true,
6606     delay: 100,
6607     stopEvent : true,
6608     forumId: 4
6609 });</code></pre>
6610      * <p>
6611      * <b>Attaching multiple handlers in 1 call</b><br>
6612       * The method also allows for a single argument to be passed which is a config object containing properties
6613      * which specify multiple handlers.
6614      * <p>
6615      * Code:<pre><code>
6616 el.on({
6617     'click' : {
6618         fn: this.onClick
6619         scope: this,
6620         delay: 100
6621     },
6622     'mouseover' : {
6623         fn: this.onMouseOver
6624         scope: this
6625     },
6626     'mouseout' : {
6627         fn: this.onMouseOut
6628         scope: this
6629     }
6630 });</code></pre>
6631      * <p>
6632      * Or a shorthand syntax:<br>
6633      * Code:<pre><code>
6634 el.on({
6635     'click' : this.onClick,
6636     'mouseover' : this.onMouseOver,
6637     'mouseout' : this.onMouseOut
6638     scope: this
6639 });</code></pre>
6640      */
6641     pub.on = pub.addListener;
6642     pub.un = pub.removeListener;
6643
6644     pub.stoppedMouseDownEvent = new Roo.util.Event();
6645     return pub;
6646 }();
6647 /**
6648   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6649   * @param {Function} fn        The method the event invokes
6650   * @param {Object}   scope    An  object that becomes the scope of the handler
6651   * @param {boolean}  override If true, the obj passed in becomes
6652   *                             the execution scope of the listener
6653   * @member Roo
6654   * @method onReady
6655  */
6656 Roo.onReady = Roo.EventManager.onDocumentReady;
6657
6658 Roo.onReady(function(){
6659     var bd = Roo.get(document.body);
6660     if(!bd){ return; }
6661
6662     var cls = [
6663             Roo.isIE ? "roo-ie"
6664             : Roo.isIE11 ? "roo-ie11"
6665             : Roo.isEdge ? "roo-edge"
6666             : Roo.isGecko ? "roo-gecko"
6667             : Roo.isOpera ? "roo-opera"
6668             : Roo.isSafari ? "roo-safari" : ""];
6669
6670     if(Roo.isMac){
6671         cls.push("roo-mac");
6672     }
6673     if(Roo.isLinux){
6674         cls.push("roo-linux");
6675     }
6676     if(Roo.isIOS){
6677         cls.push("roo-ios");
6678     }
6679     if(Roo.isTouch){
6680         cls.push("roo-touch");
6681     }
6682     if(Roo.isBorderBox){
6683         cls.push('roo-border-box');
6684     }
6685     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6686         var p = bd.dom.parentNode;
6687         if(p){
6688             p.className += ' roo-strict';
6689         }
6690     }
6691     bd.addClass(cls.join(' '));
6692 });
6693
6694 /**
6695  * @class Roo.EventObject
6696  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6697  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6698  * Example:
6699  * <pre><code>
6700  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6701     e.preventDefault();
6702     var target = e.getTarget();
6703     ...
6704  }
6705  var myDiv = Roo.get("myDiv");
6706  myDiv.on("click", handleClick);
6707  //or
6708  Roo.EventManager.on("myDiv", 'click', handleClick);
6709  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6710  </code></pre>
6711  * @singleton
6712  */
6713 Roo.EventObject = function(){
6714     
6715     var E = Roo.lib.Event;
6716     
6717     // safari keypress events for special keys return bad keycodes
6718     var safariKeys = {
6719         63234 : 37, // left
6720         63235 : 39, // right
6721         63232 : 38, // up
6722         63233 : 40, // down
6723         63276 : 33, // page up
6724         63277 : 34, // page down
6725         63272 : 46, // delete
6726         63273 : 36, // home
6727         63275 : 35  // end
6728     };
6729
6730     // normalize button clicks
6731     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6732                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6733
6734     Roo.EventObjectImpl = function(e){
6735         if(e){
6736             this.setEvent(e.browserEvent || e);
6737         }
6738     };
6739     Roo.EventObjectImpl.prototype = {
6740         /**
6741          * Used to fix doc tools.
6742          * @scope Roo.EventObject.prototype
6743          */
6744             
6745
6746         
6747         
6748         /** The normal browser event */
6749         browserEvent : null,
6750         /** The button pressed in a mouse event */
6751         button : -1,
6752         /** True if the shift key was down during the event */
6753         shiftKey : false,
6754         /** True if the control key was down during the event */
6755         ctrlKey : false,
6756         /** True if the alt key was down during the event */
6757         altKey : false,
6758
6759         /** Key constant 
6760         * @type Number */
6761         BACKSPACE : 8,
6762         /** Key constant 
6763         * @type Number */
6764         TAB : 9,
6765         /** Key constant 
6766         * @type Number */
6767         RETURN : 13,
6768         /** Key constant 
6769         * @type Number */
6770         ENTER : 13,
6771         /** Key constant 
6772         * @type Number */
6773         SHIFT : 16,
6774         /** Key constant 
6775         * @type Number */
6776         CONTROL : 17,
6777         /** Key constant 
6778         * @type Number */
6779         ESC : 27,
6780         /** Key constant 
6781         * @type Number */
6782         SPACE : 32,
6783         /** Key constant 
6784         * @type Number */
6785         PAGEUP : 33,
6786         /** Key constant 
6787         * @type Number */
6788         PAGEDOWN : 34,
6789         /** Key constant 
6790         * @type Number */
6791         END : 35,
6792         /** Key constant 
6793         * @type Number */
6794         HOME : 36,
6795         /** Key constant 
6796         * @type Number */
6797         LEFT : 37,
6798         /** Key constant 
6799         * @type Number */
6800         UP : 38,
6801         /** Key constant 
6802         * @type Number */
6803         RIGHT : 39,
6804         /** Key constant 
6805         * @type Number */
6806         DOWN : 40,
6807         /** Key constant 
6808         * @type Number */
6809         DELETE : 46,
6810         /** Key constant 
6811         * @type Number */
6812         F5 : 116,
6813
6814            /** @private */
6815         setEvent : function(e){
6816             if(e == this || (e && e.browserEvent)){ // already wrapped
6817                 return e;
6818             }
6819             this.browserEvent = e;
6820             if(e){
6821                 // normalize buttons
6822                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6823                 if(e.type == 'click' && this.button == -1){
6824                     this.button = 0;
6825                 }
6826                 this.type = e.type;
6827                 this.shiftKey = e.shiftKey;
6828                 // mac metaKey behaves like ctrlKey
6829                 this.ctrlKey = e.ctrlKey || e.metaKey;
6830                 this.altKey = e.altKey;
6831                 // in getKey these will be normalized for the mac
6832                 this.keyCode = e.keyCode;
6833                 // keyup warnings on firefox.
6834                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6835                 // cache the target for the delayed and or buffered events
6836                 this.target = E.getTarget(e);
6837                 // same for XY
6838                 this.xy = E.getXY(e);
6839             }else{
6840                 this.button = -1;
6841                 this.shiftKey = false;
6842                 this.ctrlKey = false;
6843                 this.altKey = false;
6844                 this.keyCode = 0;
6845                 this.charCode =0;
6846                 this.target = null;
6847                 this.xy = [0, 0];
6848             }
6849             return this;
6850         },
6851
6852         /**
6853          * Stop the event (preventDefault and stopPropagation)
6854          */
6855         stopEvent : function(){
6856             if(this.browserEvent){
6857                 if(this.browserEvent.type == 'mousedown'){
6858                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6859                 }
6860                 E.stopEvent(this.browserEvent);
6861             }
6862         },
6863
6864         /**
6865          * Prevents the browsers default handling of the event.
6866          */
6867         preventDefault : function(){
6868             if(this.browserEvent){
6869                 E.preventDefault(this.browserEvent);
6870             }
6871         },
6872
6873         /** @private */
6874         isNavKeyPress : function(){
6875             var k = this.keyCode;
6876             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6877             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6878         },
6879
6880         isSpecialKey : function(){
6881             var k = this.keyCode;
6882             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6883             (k == 16) || (k == 17) ||
6884             (k >= 18 && k <= 20) ||
6885             (k >= 33 && k <= 35) ||
6886             (k >= 36 && k <= 39) ||
6887             (k >= 44 && k <= 45);
6888         },
6889         /**
6890          * Cancels bubbling of the event.
6891          */
6892         stopPropagation : function(){
6893             if(this.browserEvent){
6894                 if(this.type == 'mousedown'){
6895                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6896                 }
6897                 E.stopPropagation(this.browserEvent);
6898             }
6899         },
6900
6901         /**
6902          * Gets the key code for the event.
6903          * @return {Number}
6904          */
6905         getCharCode : function(){
6906             return this.charCode || this.keyCode;
6907         },
6908
6909         /**
6910          * Returns a normalized keyCode for the event.
6911          * @return {Number} The key code
6912          */
6913         getKey : function(){
6914             var k = this.keyCode || this.charCode;
6915             return Roo.isSafari ? (safariKeys[k] || k) : k;
6916         },
6917
6918         /**
6919          * Gets the x coordinate of the event.
6920          * @return {Number}
6921          */
6922         getPageX : function(){
6923             return this.xy[0];
6924         },
6925
6926         /**
6927          * Gets the y coordinate of the event.
6928          * @return {Number}
6929          */
6930         getPageY : function(){
6931             return this.xy[1];
6932         },
6933
6934         /**
6935          * Gets the time of the event.
6936          * @return {Number}
6937          */
6938         getTime : function(){
6939             if(this.browserEvent){
6940                 return E.getTime(this.browserEvent);
6941             }
6942             return null;
6943         },
6944
6945         /**
6946          * Gets the page coordinates of the event.
6947          * @return {Array} The xy values like [x, y]
6948          */
6949         getXY : function(){
6950             return this.xy;
6951         },
6952
6953         /**
6954          * Gets the target for the event.
6955          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6956          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6957                 search as a number or element (defaults to 10 || document.body)
6958          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6959          * @return {HTMLelement}
6960          */
6961         getTarget : function(selector, maxDepth, returnEl){
6962             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6963         },
6964         /**
6965          * Gets the related target.
6966          * @return {HTMLElement}
6967          */
6968         getRelatedTarget : function(){
6969             if(this.browserEvent){
6970                 return E.getRelatedTarget(this.browserEvent);
6971             }
6972             return null;
6973         },
6974
6975         /**
6976          * Normalizes mouse wheel delta across browsers
6977          * @return {Number} The delta
6978          */
6979         getWheelDelta : function(){
6980             var e = this.browserEvent;
6981             var delta = 0;
6982             if(e.wheelDelta){ /* IE/Opera. */
6983                 delta = e.wheelDelta/120;
6984             }else if(e.detail){ /* Mozilla case. */
6985                 delta = -e.detail/3;
6986             }
6987             return delta;
6988         },
6989
6990         /**
6991          * Returns true if the control, meta, shift or alt key was pressed during this event.
6992          * @return {Boolean}
6993          */
6994         hasModifier : function(){
6995             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6996         },
6997
6998         /**
6999          * Returns true if the target of this event equals el or is a child of el
7000          * @param {String/HTMLElement/Element} el
7001          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7002          * @return {Boolean}
7003          */
7004         within : function(el, related){
7005             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7006             return t && Roo.fly(el).contains(t);
7007         },
7008
7009         getPoint : function(){
7010             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7011         }
7012     };
7013
7014     return new Roo.EventObjectImpl();
7015 }();
7016             
7017     /*
7018  * Based on:
7019  * Ext JS Library 1.1.1
7020  * Copyright(c) 2006-2007, Ext JS, LLC.
7021  *
7022  * Originally Released Under LGPL - original licence link has changed is not relivant.
7023  *
7024  * Fork - LGPL
7025  * <script type="text/javascript">
7026  */
7027
7028  
7029 // was in Composite Element!??!?!
7030  
7031 (function(){
7032     var D = Roo.lib.Dom;
7033     var E = Roo.lib.Event;
7034     var A = Roo.lib.Anim;
7035
7036     // local style camelizing for speed
7037     var propCache = {};
7038     var camelRe = /(-[a-z])/gi;
7039     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7040     var view = document.defaultView;
7041
7042 /**
7043  * @class Roo.Element
7044  * Represents an Element in the DOM.<br><br>
7045  * Usage:<br>
7046 <pre><code>
7047 var el = Roo.get("my-div");
7048
7049 // or with getEl
7050 var el = getEl("my-div");
7051
7052 // or with a DOM element
7053 var el = Roo.get(myDivElement);
7054 </code></pre>
7055  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7056  * each call instead of constructing a new one.<br><br>
7057  * <b>Animations</b><br />
7058  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7059  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7060 <pre>
7061 Option    Default   Description
7062 --------- --------  ---------------------------------------------
7063 duration  .35       The duration of the animation in seconds
7064 easing    easeOut   The YUI easing method
7065 callback  none      A function to execute when the anim completes
7066 scope     this      The scope (this) of the callback function
7067 </pre>
7068 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7069 * manipulate the animation. Here's an example:
7070 <pre><code>
7071 var el = Roo.get("my-div");
7072
7073 // no animation
7074 el.setWidth(100);
7075
7076 // default animation
7077 el.setWidth(100, true);
7078
7079 // animation with some options set
7080 el.setWidth(100, {
7081     duration: 1,
7082     callback: this.foo,
7083     scope: this
7084 });
7085
7086 // using the "anim" property to get the Anim object
7087 var opt = {
7088     duration: 1,
7089     callback: this.foo,
7090     scope: this
7091 };
7092 el.setWidth(100, opt);
7093 ...
7094 if(opt.anim.isAnimated()){
7095     opt.anim.stop();
7096 }
7097 </code></pre>
7098 * <b> Composite (Collections of) Elements</b><br />
7099  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7100  * @constructor Create a new Element directly.
7101  * @param {String/HTMLElement} element
7102  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7103  */
7104     Roo.Element = function(element, forceNew){
7105         var dom = typeof element == "string" ?
7106                 document.getElementById(element) : element;
7107         if(!dom){ // invalid id/element
7108             return null;
7109         }
7110         var id = dom.id;
7111         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7112             return Roo.Element.cache[id];
7113         }
7114
7115         /**
7116          * The DOM element
7117          * @type HTMLElement
7118          */
7119         this.dom = dom;
7120
7121         /**
7122          * The DOM element ID
7123          * @type String
7124          */
7125         this.id = id || Roo.id(dom);
7126     };
7127
7128     var El = Roo.Element;
7129
7130     El.prototype = {
7131         /**
7132          * The element's default display mode  (defaults to "")
7133          * @type String
7134          */
7135         originalDisplay : "",
7136
7137         visibilityMode : 1,
7138         /**
7139          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7140          * @type String
7141          */
7142         defaultUnit : "px",
7143         
7144         /**
7145          * Sets the element's visibility mode. When setVisible() is called it
7146          * will use this to determine whether to set the visibility or the display property.
7147          * @param visMode Element.VISIBILITY or Element.DISPLAY
7148          * @return {Roo.Element} this
7149          */
7150         setVisibilityMode : function(visMode){
7151             this.visibilityMode = visMode;
7152             return this;
7153         },
7154         /**
7155          * Convenience method for setVisibilityMode(Element.DISPLAY)
7156          * @param {String} display (optional) What to set display to when visible
7157          * @return {Roo.Element} this
7158          */
7159         enableDisplayMode : function(display){
7160             this.setVisibilityMode(El.DISPLAY);
7161             if(typeof display != "undefined") { this.originalDisplay = display; }
7162             return this;
7163         },
7164
7165         /**
7166          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7167          * @param {String} selector The simple selector to test
7168          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7169                 search as a number or element (defaults to 10 || document.body)
7170          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7171          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7172          */
7173         findParent : function(simpleSelector, maxDepth, returnEl){
7174             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7175             maxDepth = maxDepth || 50;
7176             if(typeof maxDepth != "number"){
7177                 stopEl = Roo.getDom(maxDepth);
7178                 maxDepth = 10;
7179             }
7180             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7181                 if(dq.is(p, simpleSelector)){
7182                     return returnEl ? Roo.get(p) : p;
7183                 }
7184                 depth++;
7185                 p = p.parentNode;
7186             }
7187             return null;
7188         },
7189
7190
7191         /**
7192          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7193          * @param {String} selector The simple selector to test
7194          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7195                 search as a number or element (defaults to 10 || document.body)
7196          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7197          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7198          */
7199         findParentNode : function(simpleSelector, maxDepth, returnEl){
7200             var p = Roo.fly(this.dom.parentNode, '_internal');
7201             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7202         },
7203         
7204         /**
7205          * Looks at  the scrollable parent element
7206          */
7207         findScrollableParent : function()
7208         {
7209             var overflowRegex = /(auto|scroll)/;
7210             
7211             if(this.getStyle('position') === 'fixed'){
7212                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7213             }
7214             
7215             var excludeStaticParent = this.getStyle('position') === "absolute";
7216             
7217             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7218                 
7219                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7220                     continue;
7221                 }
7222                 
7223                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7224                     return parent;
7225                 }
7226                 
7227                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7228                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7229                 }
7230             }
7231             
7232             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7233         },
7234
7235         /**
7236          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7237          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7238          * @param {String} selector The simple selector to test
7239          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7240                 search as a number or element (defaults to 10 || document.body)
7241          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7242          */
7243         up : function(simpleSelector, maxDepth){
7244             return this.findParentNode(simpleSelector, maxDepth, true);
7245         },
7246
7247
7248
7249         /**
7250          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7251          * @param {String} selector The simple selector to test
7252          * @return {Boolean} True if this element matches the selector, else false
7253          */
7254         is : function(simpleSelector){
7255             return Roo.DomQuery.is(this.dom, simpleSelector);
7256         },
7257
7258         /**
7259          * Perform animation on this element.
7260          * @param {Object} args The YUI animation control args
7261          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7262          * @param {Function} onComplete (optional) Function to call when animation completes
7263          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7264          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7265          * @return {Roo.Element} this
7266          */
7267         animate : function(args, duration, onComplete, easing, animType){
7268             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7269             return this;
7270         },
7271
7272         /*
7273          * @private Internal animation call
7274          */
7275         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7276             animType = animType || 'run';
7277             opt = opt || {};
7278             var anim = Roo.lib.Anim[animType](
7279                 this.dom, args,
7280                 (opt.duration || defaultDur) || .35,
7281                 (opt.easing || defaultEase) || 'easeOut',
7282                 function(){
7283                     Roo.callback(cb, this);
7284                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7285                 },
7286                 this
7287             );
7288             opt.anim = anim;
7289             return anim;
7290         },
7291
7292         // private legacy anim prep
7293         preanim : function(a, i){
7294             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7295         },
7296
7297         /**
7298          * Removes worthless text nodes
7299          * @param {Boolean} forceReclean (optional) By default the element
7300          * keeps track if it has been cleaned already so
7301          * you can call this over and over. However, if you update the element and
7302          * need to force a reclean, you can pass true.
7303          */
7304         clean : function(forceReclean){
7305             if(this.isCleaned && forceReclean !== true){
7306                 return this;
7307             }
7308             var ns = /\S/;
7309             var d = this.dom, n = d.firstChild, ni = -1;
7310             while(n){
7311                 var nx = n.nextSibling;
7312                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7313                     d.removeChild(n);
7314                 }else{
7315                     n.nodeIndex = ++ni;
7316                 }
7317                 n = nx;
7318             }
7319             this.isCleaned = true;
7320             return this;
7321         },
7322
7323         // private
7324         calcOffsetsTo : function(el){
7325             el = Roo.get(el);
7326             var d = el.dom;
7327             var restorePos = false;
7328             if(el.getStyle('position') == 'static'){
7329                 el.position('relative');
7330                 restorePos = true;
7331             }
7332             var x = 0, y =0;
7333             var op = this.dom;
7334             while(op && op != d && op.tagName != 'HTML'){
7335                 x+= op.offsetLeft;
7336                 y+= op.offsetTop;
7337                 op = op.offsetParent;
7338             }
7339             if(restorePos){
7340                 el.position('static');
7341             }
7342             return [x, y];
7343         },
7344
7345         /**
7346          * Scrolls this element into view within the passed container.
7347          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7348          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7349          * @return {Roo.Element} this
7350          */
7351         scrollIntoView : function(container, hscroll){
7352             var c = Roo.getDom(container) || document.body;
7353             var el = this.dom;
7354
7355             var o = this.calcOffsetsTo(c),
7356                 l = o[0],
7357                 t = o[1],
7358                 b = t+el.offsetHeight,
7359                 r = l+el.offsetWidth;
7360
7361             var ch = c.clientHeight;
7362             var ct = parseInt(c.scrollTop, 10);
7363             var cl = parseInt(c.scrollLeft, 10);
7364             var cb = ct + ch;
7365             var cr = cl + c.clientWidth;
7366
7367             if(t < ct){
7368                 c.scrollTop = t;
7369             }else if(b > cb){
7370                 c.scrollTop = b-ch;
7371             }
7372
7373             if(hscroll !== false){
7374                 if(l < cl){
7375                     c.scrollLeft = l;
7376                 }else if(r > cr){
7377                     c.scrollLeft = r-c.clientWidth;
7378                 }
7379             }
7380             return this;
7381         },
7382
7383         // private
7384         scrollChildIntoView : function(child, hscroll){
7385             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7386         },
7387
7388         /**
7389          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7390          * the new height may not be available immediately.
7391          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7392          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7393          * @param {Function} onComplete (optional) Function to call when animation completes
7394          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7395          * @return {Roo.Element} this
7396          */
7397         autoHeight : function(animate, duration, onComplete, easing){
7398             var oldHeight = this.getHeight();
7399             this.clip();
7400             this.setHeight(1); // force clipping
7401             setTimeout(function(){
7402                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7403                 if(!animate){
7404                     this.setHeight(height);
7405                     this.unclip();
7406                     if(typeof onComplete == "function"){
7407                         onComplete();
7408                     }
7409                 }else{
7410                     this.setHeight(oldHeight); // restore original height
7411                     this.setHeight(height, animate, duration, function(){
7412                         this.unclip();
7413                         if(typeof onComplete == "function") { onComplete(); }
7414                     }.createDelegate(this), easing);
7415                 }
7416             }.createDelegate(this), 0);
7417             return this;
7418         },
7419
7420         /**
7421          * Returns true if this element is an ancestor of the passed element
7422          * @param {HTMLElement/String} el The element to check
7423          * @return {Boolean} True if this element is an ancestor of el, else false
7424          */
7425         contains : function(el){
7426             if(!el){return false;}
7427             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7428         },
7429
7430         /**
7431          * Checks whether the element is currently visible using both visibility and display properties.
7432          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7433          * @return {Boolean} True if the element is currently visible, else false
7434          */
7435         isVisible : function(deep) {
7436             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7437             if(deep !== true || !vis){
7438                 return vis;
7439             }
7440             var p = this.dom.parentNode;
7441             while(p && p.tagName.toLowerCase() != "body"){
7442                 if(!Roo.fly(p, '_isVisible').isVisible()){
7443                     return false;
7444                 }
7445                 p = p.parentNode;
7446             }
7447             return true;
7448         },
7449
7450         /**
7451          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7452          * @param {String} selector The CSS selector
7453          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7454          * @return {CompositeElement/CompositeElementLite} The composite element
7455          */
7456         select : function(selector, unique){
7457             return El.select(selector, unique, this.dom);
7458         },
7459
7460         /**
7461          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7462          * @param {String} selector The CSS selector
7463          * @return {Array} An array of the matched nodes
7464          */
7465         query : function(selector, unique){
7466             return Roo.DomQuery.select(selector, this.dom);
7467         },
7468
7469         /**
7470          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7471          * @param {String} selector The CSS selector
7472          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7473          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7474          */
7475         child : function(selector, returnDom){
7476             var n = Roo.DomQuery.selectNode(selector, this.dom);
7477             return returnDom ? n : Roo.get(n);
7478         },
7479
7480         /**
7481          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7482          * @param {String} selector The CSS selector
7483          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7484          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7485          */
7486         down : function(selector, returnDom){
7487             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7488             return returnDom ? n : Roo.get(n);
7489         },
7490
7491         /**
7492          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7493          * @param {String} group The group the DD object is member of
7494          * @param {Object} config The DD config object
7495          * @param {Object} overrides An object containing methods to override/implement on the DD object
7496          * @return {Roo.dd.DD} The DD object
7497          */
7498         initDD : function(group, config, overrides){
7499             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7500             return Roo.apply(dd, overrides);
7501         },
7502
7503         /**
7504          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7505          * @param {String} group The group the DDProxy object is member of
7506          * @param {Object} config The DDProxy config object
7507          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7508          * @return {Roo.dd.DDProxy} The DDProxy object
7509          */
7510         initDDProxy : function(group, config, overrides){
7511             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7512             return Roo.apply(dd, overrides);
7513         },
7514
7515         /**
7516          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7517          * @param {String} group The group the DDTarget object is member of
7518          * @param {Object} config The DDTarget config object
7519          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7520          * @return {Roo.dd.DDTarget} The DDTarget object
7521          */
7522         initDDTarget : function(group, config, overrides){
7523             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7524             return Roo.apply(dd, overrides);
7525         },
7526
7527         /**
7528          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7529          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7530          * @param {Boolean} visible Whether the element is visible
7531          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7532          * @return {Roo.Element} this
7533          */
7534          setVisible : function(visible, animate){
7535             if(!animate || !A){
7536                 if(this.visibilityMode == El.DISPLAY){
7537                     this.setDisplayed(visible);
7538                 }else{
7539                     this.fixDisplay();
7540                     this.dom.style.visibility = visible ? "visible" : "hidden";
7541                 }
7542             }else{
7543                 // closure for composites
7544                 var dom = this.dom;
7545                 var visMode = this.visibilityMode;
7546                 if(visible){
7547                     this.setOpacity(.01);
7548                     this.setVisible(true);
7549                 }
7550                 this.anim({opacity: { to: (visible?1:0) }},
7551                       this.preanim(arguments, 1),
7552                       null, .35, 'easeIn', function(){
7553                          if(!visible){
7554                              if(visMode == El.DISPLAY){
7555                                  dom.style.display = "none";
7556                              }else{
7557                                  dom.style.visibility = "hidden";
7558                              }
7559                              Roo.get(dom).setOpacity(1);
7560                          }
7561                      });
7562             }
7563             return this;
7564         },
7565
7566         /**
7567          * Returns true if display is not "none"
7568          * @return {Boolean}
7569          */
7570         isDisplayed : function() {
7571             return this.getStyle("display") != "none";
7572         },
7573
7574         /**
7575          * Toggles the element's visibility or display, depending on visibility mode.
7576          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7577          * @return {Roo.Element} this
7578          */
7579         toggle : function(animate){
7580             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7581             return this;
7582         },
7583
7584         /**
7585          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7586          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7587          * @return {Roo.Element} this
7588          */
7589         setDisplayed : function(value) {
7590             if(typeof value == "boolean"){
7591                value = value ? this.originalDisplay : "none";
7592             }
7593             this.setStyle("display", value);
7594             return this;
7595         },
7596
7597         /**
7598          * Tries to focus the element. Any exceptions are caught and ignored.
7599          * @return {Roo.Element} this
7600          */
7601         focus : function() {
7602             try{
7603                 this.dom.focus();
7604             }catch(e){}
7605             return this;
7606         },
7607
7608         /**
7609          * Tries to blur the element. Any exceptions are caught and ignored.
7610          * @return {Roo.Element} this
7611          */
7612         blur : function() {
7613             try{
7614                 this.dom.blur();
7615             }catch(e){}
7616             return this;
7617         },
7618
7619         /**
7620          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7621          * @param {String/Array} className The CSS class to add, or an array of classes
7622          * @return {Roo.Element} this
7623          */
7624         addClass : function(className){
7625             if(className instanceof Array){
7626                 for(var i = 0, len = className.length; i < len; i++) {
7627                     this.addClass(className[i]);
7628                 }
7629             }else{
7630                 if(className && !this.hasClass(className)){
7631                     this.dom.className = this.dom.className + " " + className;
7632                 }
7633             }
7634             return this;
7635         },
7636
7637         /**
7638          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7639          * @param {String/Array} className The CSS class to add, or an array of classes
7640          * @return {Roo.Element} this
7641          */
7642         radioClass : function(className){
7643             var siblings = this.dom.parentNode.childNodes;
7644             for(var i = 0; i < siblings.length; i++) {
7645                 var s = siblings[i];
7646                 if(s.nodeType == 1){
7647                     Roo.get(s).removeClass(className);
7648                 }
7649             }
7650             this.addClass(className);
7651             return this;
7652         },
7653
7654         /**
7655          * Removes one or more CSS classes from the element.
7656          * @param {String/Array} className The CSS class to remove, or an array of classes
7657          * @return {Roo.Element} this
7658          */
7659         removeClass : function(className){
7660             if(!className || !this.dom.className){
7661                 return this;
7662             }
7663             if(className instanceof Array){
7664                 for(var i = 0, len = className.length; i < len; i++) {
7665                     this.removeClass(className[i]);
7666                 }
7667             }else{
7668                 if(this.hasClass(className)){
7669                     var re = this.classReCache[className];
7670                     if (!re) {
7671                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7672                        this.classReCache[className] = re;
7673                     }
7674                     this.dom.className =
7675                         this.dom.className.replace(re, " ");
7676                 }
7677             }
7678             return this;
7679         },
7680
7681         // private
7682         classReCache: {},
7683
7684         /**
7685          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7686          * @param {String} className The CSS class to toggle
7687          * @return {Roo.Element} this
7688          */
7689         toggleClass : function(className){
7690             if(this.hasClass(className)){
7691                 this.removeClass(className);
7692             }else{
7693                 this.addClass(className);
7694             }
7695             return this;
7696         },
7697
7698         /**
7699          * Checks if the specified CSS class exists on this element's DOM node.
7700          * @param {String} className The CSS class to check for
7701          * @return {Boolean} True if the class exists, else false
7702          */
7703         hasClass : function(className){
7704             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7705         },
7706
7707         /**
7708          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7709          * @param {String} oldClassName The CSS class to replace
7710          * @param {String} newClassName The replacement CSS class
7711          * @return {Roo.Element} this
7712          */
7713         replaceClass : function(oldClassName, newClassName){
7714             this.removeClass(oldClassName);
7715             this.addClass(newClassName);
7716             return this;
7717         },
7718
7719         /**
7720          * Returns an object with properties matching the styles requested.
7721          * For example, el.getStyles('color', 'font-size', 'width') might return
7722          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7723          * @param {String} style1 A style name
7724          * @param {String} style2 A style name
7725          * @param {String} etc.
7726          * @return {Object} The style object
7727          */
7728         getStyles : function(){
7729             var a = arguments, len = a.length, r = {};
7730             for(var i = 0; i < len; i++){
7731                 r[a[i]] = this.getStyle(a[i]);
7732             }
7733             return r;
7734         },
7735
7736         /**
7737          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7738          * @param {String} property The style property whose value is returned.
7739          * @return {String} The current value of the style property for this element.
7740          */
7741         getStyle : function(){
7742             return view && view.getComputedStyle ?
7743                 function(prop){
7744                     var el = this.dom, v, cs, camel;
7745                     if(prop == 'float'){
7746                         prop = "cssFloat";
7747                     }
7748                     if(el.style && (v = el.style[prop])){
7749                         return v;
7750                     }
7751                     if(cs = view.getComputedStyle(el, "")){
7752                         if(!(camel = propCache[prop])){
7753                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7754                         }
7755                         return cs[camel];
7756                     }
7757                     return null;
7758                 } :
7759                 function(prop){
7760                     var el = this.dom, v, cs, camel;
7761                     if(prop == 'opacity'){
7762                         if(typeof el.style.filter == 'string'){
7763                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7764                             if(m){
7765                                 var fv = parseFloat(m[1]);
7766                                 if(!isNaN(fv)){
7767                                     return fv ? fv / 100 : 0;
7768                                 }
7769                             }
7770                         }
7771                         return 1;
7772                     }else if(prop == 'float'){
7773                         prop = "styleFloat";
7774                     }
7775                     if(!(camel = propCache[prop])){
7776                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7777                     }
7778                     if(v = el.style[camel]){
7779                         return v;
7780                     }
7781                     if(cs = el.currentStyle){
7782                         return cs[camel];
7783                     }
7784                     return null;
7785                 };
7786         }(),
7787
7788         /**
7789          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7790          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7791          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7792          * @return {Roo.Element} this
7793          */
7794         setStyle : function(prop, value){
7795             if(typeof prop == "string"){
7796                 
7797                 if (prop == 'float') {
7798                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7799                     return this;
7800                 }
7801                 
7802                 var camel;
7803                 if(!(camel = propCache[prop])){
7804                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7805                 }
7806                 
7807                 if(camel == 'opacity') {
7808                     this.setOpacity(value);
7809                 }else{
7810                     this.dom.style[camel] = value;
7811                 }
7812             }else{
7813                 for(var style in prop){
7814                     if(typeof prop[style] != "function"){
7815                        this.setStyle(style, prop[style]);
7816                     }
7817                 }
7818             }
7819             return this;
7820         },
7821
7822         /**
7823          * More flexible version of {@link #setStyle} for setting style properties.
7824          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7825          * a function which returns such a specification.
7826          * @return {Roo.Element} this
7827          */
7828         applyStyles : function(style){
7829             Roo.DomHelper.applyStyles(this.dom, style);
7830             return this;
7831         },
7832
7833         /**
7834           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7835           * @return {Number} The X position of the element
7836           */
7837         getX : function(){
7838             return D.getX(this.dom);
7839         },
7840
7841         /**
7842           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7843           * @return {Number} The Y position of the element
7844           */
7845         getY : function(){
7846             return D.getY(this.dom);
7847         },
7848
7849         /**
7850           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7851           * @return {Array} The XY position of the element
7852           */
7853         getXY : function(){
7854             return D.getXY(this.dom);
7855         },
7856
7857         /**
7858          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7859          * @param {Number} The X position of the element
7860          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7861          * @return {Roo.Element} this
7862          */
7863         setX : function(x, animate){
7864             if(!animate || !A){
7865                 D.setX(this.dom, x);
7866             }else{
7867                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7868             }
7869             return this;
7870         },
7871
7872         /**
7873          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7874          * @param {Number} The Y position of the element
7875          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7876          * @return {Roo.Element} this
7877          */
7878         setY : function(y, animate){
7879             if(!animate || !A){
7880                 D.setY(this.dom, y);
7881             }else{
7882                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7883             }
7884             return this;
7885         },
7886
7887         /**
7888          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7889          * @param {String} left The left CSS property value
7890          * @return {Roo.Element} this
7891          */
7892         setLeft : function(left){
7893             this.setStyle("left", this.addUnits(left));
7894             return this;
7895         },
7896
7897         /**
7898          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7899          * @param {String} top The top CSS property value
7900          * @return {Roo.Element} this
7901          */
7902         setTop : function(top){
7903             this.setStyle("top", this.addUnits(top));
7904             return this;
7905         },
7906
7907         /**
7908          * Sets the element's CSS right style.
7909          * @param {String} right The right CSS property value
7910          * @return {Roo.Element} this
7911          */
7912         setRight : function(right){
7913             this.setStyle("right", this.addUnits(right));
7914             return this;
7915         },
7916
7917         /**
7918          * Sets the element's CSS bottom style.
7919          * @param {String} bottom The bottom CSS property value
7920          * @return {Roo.Element} this
7921          */
7922         setBottom : function(bottom){
7923             this.setStyle("bottom", this.addUnits(bottom));
7924             return this;
7925         },
7926
7927         /**
7928          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7929          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7930          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7931          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7932          * @return {Roo.Element} this
7933          */
7934         setXY : function(pos, animate){
7935             if(!animate || !A){
7936                 D.setXY(this.dom, pos);
7937             }else{
7938                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7939             }
7940             return this;
7941         },
7942
7943         /**
7944          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7945          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7946          * @param {Number} x X value for new position (coordinates are page-based)
7947          * @param {Number} y Y value for new position (coordinates are page-based)
7948          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7949          * @return {Roo.Element} this
7950          */
7951         setLocation : function(x, y, animate){
7952             this.setXY([x, y], this.preanim(arguments, 2));
7953             return this;
7954         },
7955
7956         /**
7957          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7958          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7959          * @param {Number} x X value for new position (coordinates are page-based)
7960          * @param {Number} y Y value for new position (coordinates are page-based)
7961          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7962          * @return {Roo.Element} this
7963          */
7964         moveTo : function(x, y, animate){
7965             this.setXY([x, y], this.preanim(arguments, 2));
7966             return this;
7967         },
7968
7969         /**
7970          * Returns the region of the given element.
7971          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7972          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7973          */
7974         getRegion : function(){
7975             return D.getRegion(this.dom);
7976         },
7977
7978         /**
7979          * Returns the offset height of the element
7980          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7981          * @return {Number} The element's height
7982          */
7983         getHeight : function(contentHeight){
7984             var h = this.dom.offsetHeight || 0;
7985             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7986         },
7987
7988         /**
7989          * Returns the offset width of the element
7990          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7991          * @return {Number} The element's width
7992          */
7993         getWidth : function(contentWidth){
7994             var w = this.dom.offsetWidth || 0;
7995             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7996         },
7997
7998         /**
7999          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8000          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8001          * if a height has not been set using CSS.
8002          * @return {Number}
8003          */
8004         getComputedHeight : function(){
8005             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8006             if(!h){
8007                 h = parseInt(this.getStyle('height'), 10) || 0;
8008                 if(!this.isBorderBox()){
8009                     h += this.getFrameWidth('tb');
8010                 }
8011             }
8012             return h;
8013         },
8014
8015         /**
8016          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8017          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8018          * if a width has not been set using CSS.
8019          * @return {Number}
8020          */
8021         getComputedWidth : function(){
8022             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8023             if(!w){
8024                 w = parseInt(this.getStyle('width'), 10) || 0;
8025                 if(!this.isBorderBox()){
8026                     w += this.getFrameWidth('lr');
8027                 }
8028             }
8029             return w;
8030         },
8031
8032         /**
8033          * Returns the size of the element.
8034          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8035          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8036          */
8037         getSize : function(contentSize){
8038             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8039         },
8040
8041         /**
8042          * Returns the width and height of the viewport.
8043          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8044          */
8045         getViewSize : function(){
8046             var d = this.dom, doc = document, aw = 0, ah = 0;
8047             if(d == doc || d == doc.body){
8048                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8049             }else{
8050                 return {
8051                     width : d.clientWidth,
8052                     height: d.clientHeight
8053                 };
8054             }
8055         },
8056
8057         /**
8058          * Returns the value of the "value" attribute
8059          * @param {Boolean} asNumber true to parse the value as a number
8060          * @return {String/Number}
8061          */
8062         getValue : function(asNumber){
8063             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8064         },
8065
8066         // private
8067         adjustWidth : function(width){
8068             if(typeof width == "number"){
8069                 if(this.autoBoxAdjust && !this.isBorderBox()){
8070                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8071                 }
8072                 if(width < 0){
8073                     width = 0;
8074                 }
8075             }
8076             return width;
8077         },
8078
8079         // private
8080         adjustHeight : function(height){
8081             if(typeof height == "number"){
8082                if(this.autoBoxAdjust && !this.isBorderBox()){
8083                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8084                }
8085                if(height < 0){
8086                    height = 0;
8087                }
8088             }
8089             return height;
8090         },
8091
8092         /**
8093          * Set the width of the element
8094          * @param {Number} width The new width
8095          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8096          * @return {Roo.Element} this
8097          */
8098         setWidth : function(width, animate){
8099             width = this.adjustWidth(width);
8100             if(!animate || !A){
8101                 this.dom.style.width = this.addUnits(width);
8102             }else{
8103                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8104             }
8105             return this;
8106         },
8107
8108         /**
8109          * Set the height of the element
8110          * @param {Number} height The new height
8111          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8112          * @return {Roo.Element} this
8113          */
8114          setHeight : function(height, animate){
8115             height = this.adjustHeight(height);
8116             if(!animate || !A){
8117                 this.dom.style.height = this.addUnits(height);
8118             }else{
8119                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8120             }
8121             return this;
8122         },
8123
8124         /**
8125          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8126          * @param {Number} width The new width
8127          * @param {Number} height The new height
8128          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8129          * @return {Roo.Element} this
8130          */
8131          setSize : function(width, height, animate){
8132             if(typeof width == "object"){ // in case of object from getSize()
8133                 height = width.height; width = width.width;
8134             }
8135             width = this.adjustWidth(width); height = this.adjustHeight(height);
8136             if(!animate || !A){
8137                 this.dom.style.width = this.addUnits(width);
8138                 this.dom.style.height = this.addUnits(height);
8139             }else{
8140                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8141             }
8142             return this;
8143         },
8144
8145         /**
8146          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8147          * @param {Number} x X value for new position (coordinates are page-based)
8148          * @param {Number} y Y value for new position (coordinates are page-based)
8149          * @param {Number} width The new width
8150          * @param {Number} height The new height
8151          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8152          * @return {Roo.Element} this
8153          */
8154         setBounds : function(x, y, width, height, animate){
8155             if(!animate || !A){
8156                 this.setSize(width, height);
8157                 this.setLocation(x, y);
8158             }else{
8159                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8160                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8161                               this.preanim(arguments, 4), 'motion');
8162             }
8163             return this;
8164         },
8165
8166         /**
8167          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8168          * @param {Roo.lib.Region} region The region to fill
8169          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8170          * @return {Roo.Element} this
8171          */
8172         setRegion : function(region, animate){
8173             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8174             return this;
8175         },
8176
8177         /**
8178          * Appends an event handler
8179          *
8180          * @param {String}   eventName     The type of event to append
8181          * @param {Function} fn        The method the event invokes
8182          * @param {Object} scope       (optional) The scope (this object) of the fn
8183          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8184          */
8185         addListener : function(eventName, fn, scope, options){
8186             if (this.dom) {
8187                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8188             }
8189         },
8190
8191         /**
8192          * Removes an event handler from this element
8193          * @param {String} eventName the type of event to remove
8194          * @param {Function} fn the method the event invokes
8195          * @return {Roo.Element} this
8196          */
8197         removeListener : function(eventName, fn){
8198             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8199             return this;
8200         },
8201
8202         /**
8203          * Removes all previous added listeners from this element
8204          * @return {Roo.Element} this
8205          */
8206         removeAllListeners : function(){
8207             E.purgeElement(this.dom);
8208             return this;
8209         },
8210
8211         relayEvent : function(eventName, observable){
8212             this.on(eventName, function(e){
8213                 observable.fireEvent(eventName, e);
8214             });
8215         },
8216
8217         /**
8218          * Set the opacity of the element
8219          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8220          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8221          * @return {Roo.Element} this
8222          */
8223          setOpacity : function(opacity, animate){
8224             if(!animate || !A){
8225                 var s = this.dom.style;
8226                 if(Roo.isIE){
8227                     s.zoom = 1;
8228                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8229                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8230                 }else{
8231                     s.opacity = opacity;
8232                 }
8233             }else{
8234                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8235             }
8236             return this;
8237         },
8238
8239         /**
8240          * Gets the left X coordinate
8241          * @param {Boolean} local True to get the local css position instead of page coordinate
8242          * @return {Number}
8243          */
8244         getLeft : function(local){
8245             if(!local){
8246                 return this.getX();
8247             }else{
8248                 return parseInt(this.getStyle("left"), 10) || 0;
8249             }
8250         },
8251
8252         /**
8253          * Gets the right X coordinate of the element (element X position + element width)
8254          * @param {Boolean} local True to get the local css position instead of page coordinate
8255          * @return {Number}
8256          */
8257         getRight : function(local){
8258             if(!local){
8259                 return this.getX() + this.getWidth();
8260             }else{
8261                 return (this.getLeft(true) + this.getWidth()) || 0;
8262             }
8263         },
8264
8265         /**
8266          * Gets the top Y coordinate
8267          * @param {Boolean} local True to get the local css position instead of page coordinate
8268          * @return {Number}
8269          */
8270         getTop : function(local) {
8271             if(!local){
8272                 return this.getY();
8273             }else{
8274                 return parseInt(this.getStyle("top"), 10) || 0;
8275             }
8276         },
8277
8278         /**
8279          * Gets the bottom Y coordinate of the element (element Y position + element height)
8280          * @param {Boolean} local True to get the local css position instead of page coordinate
8281          * @return {Number}
8282          */
8283         getBottom : function(local){
8284             if(!local){
8285                 return this.getY() + this.getHeight();
8286             }else{
8287                 return (this.getTop(true) + this.getHeight()) || 0;
8288             }
8289         },
8290
8291         /**
8292         * Initializes positioning on this element. If a desired position is not passed, it will make the
8293         * the element positioned relative IF it is not already positioned.
8294         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8295         * @param {Number} zIndex (optional) The zIndex to apply
8296         * @param {Number} x (optional) Set the page X position
8297         * @param {Number} y (optional) Set the page Y position
8298         */
8299         position : function(pos, zIndex, x, y){
8300             if(!pos){
8301                if(this.getStyle('position') == 'static'){
8302                    this.setStyle('position', 'relative');
8303                }
8304             }else{
8305                 this.setStyle("position", pos);
8306             }
8307             if(zIndex){
8308                 this.setStyle("z-index", zIndex);
8309             }
8310             if(x !== undefined && y !== undefined){
8311                 this.setXY([x, y]);
8312             }else if(x !== undefined){
8313                 this.setX(x);
8314             }else if(y !== undefined){
8315                 this.setY(y);
8316             }
8317         },
8318
8319         /**
8320         * Clear positioning back to the default when the document was loaded
8321         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8322         * @return {Roo.Element} this
8323          */
8324         clearPositioning : function(value){
8325             value = value ||'';
8326             this.setStyle({
8327                 "left": value,
8328                 "right": value,
8329                 "top": value,
8330                 "bottom": value,
8331                 "z-index": "",
8332                 "position" : "static"
8333             });
8334             return this;
8335         },
8336
8337         /**
8338         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8339         * snapshot before performing an update and then restoring the element.
8340         * @return {Object}
8341         */
8342         getPositioning : function(){
8343             var l = this.getStyle("left");
8344             var t = this.getStyle("top");
8345             return {
8346                 "position" : this.getStyle("position"),
8347                 "left" : l,
8348                 "right" : l ? "" : this.getStyle("right"),
8349                 "top" : t,
8350                 "bottom" : t ? "" : this.getStyle("bottom"),
8351                 "z-index" : this.getStyle("z-index")
8352             };
8353         },
8354
8355         /**
8356          * Gets the width of the border(s) for the specified side(s)
8357          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8358          * passing lr would get the border (l)eft width + the border (r)ight width.
8359          * @return {Number} The width of the sides passed added together
8360          */
8361         getBorderWidth : function(side){
8362             return this.addStyles(side, El.borders);
8363         },
8364
8365         /**
8366          * Gets the width of the padding(s) for the specified side(s)
8367          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8368          * passing lr would get the padding (l)eft + the padding (r)ight.
8369          * @return {Number} The padding of the sides passed added together
8370          */
8371         getPadding : function(side){
8372             return this.addStyles(side, El.paddings);
8373         },
8374
8375         /**
8376         * Set positioning with an object returned by getPositioning().
8377         * @param {Object} posCfg
8378         * @return {Roo.Element} this
8379          */
8380         setPositioning : function(pc){
8381             this.applyStyles(pc);
8382             if(pc.right == "auto"){
8383                 this.dom.style.right = "";
8384             }
8385             if(pc.bottom == "auto"){
8386                 this.dom.style.bottom = "";
8387             }
8388             return this;
8389         },
8390
8391         // private
8392         fixDisplay : function(){
8393             if(this.getStyle("display") == "none"){
8394                 this.setStyle("visibility", "hidden");
8395                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8396                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8397                     this.setStyle("display", "block");
8398                 }
8399             }
8400         },
8401
8402         /**
8403          * Quick set left and top adding default units
8404          * @param {String} left The left CSS property value
8405          * @param {String} top The top CSS property value
8406          * @return {Roo.Element} this
8407          */
8408          setLeftTop : function(left, top){
8409             this.dom.style.left = this.addUnits(left);
8410             this.dom.style.top = this.addUnits(top);
8411             return this;
8412         },
8413
8414         /**
8415          * Move this element relative to its current position.
8416          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8417          * @param {Number} distance How far to move the element in pixels
8418          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8419          * @return {Roo.Element} this
8420          */
8421          move : function(direction, distance, animate){
8422             var xy = this.getXY();
8423             direction = direction.toLowerCase();
8424             switch(direction){
8425                 case "l":
8426                 case "left":
8427                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8428                     break;
8429                case "r":
8430                case "right":
8431                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8432                     break;
8433                case "t":
8434                case "top":
8435                case "up":
8436                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8437                     break;
8438                case "b":
8439                case "bottom":
8440                case "down":
8441                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8442                     break;
8443             }
8444             return this;
8445         },
8446
8447         /**
8448          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8449          * @return {Roo.Element} this
8450          */
8451         clip : function(){
8452             if(!this.isClipped){
8453                this.isClipped = true;
8454                this.originalClip = {
8455                    "o": this.getStyle("overflow"),
8456                    "x": this.getStyle("overflow-x"),
8457                    "y": this.getStyle("overflow-y")
8458                };
8459                this.setStyle("overflow", "hidden");
8460                this.setStyle("overflow-x", "hidden");
8461                this.setStyle("overflow-y", "hidden");
8462             }
8463             return this;
8464         },
8465
8466         /**
8467          *  Return clipping (overflow) to original clipping before clip() was called
8468          * @return {Roo.Element} this
8469          */
8470         unclip : function(){
8471             if(this.isClipped){
8472                 this.isClipped = false;
8473                 var o = this.originalClip;
8474                 if(o.o){this.setStyle("overflow", o.o);}
8475                 if(o.x){this.setStyle("overflow-x", o.x);}
8476                 if(o.y){this.setStyle("overflow-y", o.y);}
8477             }
8478             return this;
8479         },
8480
8481
8482         /**
8483          * Gets the x,y coordinates specified by the anchor position on the element.
8484          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8485          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8486          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8487          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8488          * @return {Array} [x, y] An array containing the element's x and y coordinates
8489          */
8490         getAnchorXY : function(anchor, local, s){
8491             //Passing a different size is useful for pre-calculating anchors,
8492             //especially for anchored animations that change the el size.
8493
8494             var w, h, vp = false;
8495             if(!s){
8496                 var d = this.dom;
8497                 if(d == document.body || d == document){
8498                     vp = true;
8499                     w = D.getViewWidth(); h = D.getViewHeight();
8500                 }else{
8501                     w = this.getWidth(); h = this.getHeight();
8502                 }
8503             }else{
8504                 w = s.width;  h = s.height;
8505             }
8506             var x = 0, y = 0, r = Math.round;
8507             switch((anchor || "tl").toLowerCase()){
8508                 case "c":
8509                     x = r(w*.5);
8510                     y = r(h*.5);
8511                 break;
8512                 case "t":
8513                     x = r(w*.5);
8514                     y = 0;
8515                 break;
8516                 case "l":
8517                     x = 0;
8518                     y = r(h*.5);
8519                 break;
8520                 case "r":
8521                     x = w;
8522                     y = r(h*.5);
8523                 break;
8524                 case "b":
8525                     x = r(w*.5);
8526                     y = h;
8527                 break;
8528                 case "tl":
8529                     x = 0;
8530                     y = 0;
8531                 break;
8532                 case "bl":
8533                     x = 0;
8534                     y = h;
8535                 break;
8536                 case "br":
8537                     x = w;
8538                     y = h;
8539                 break;
8540                 case "tr":
8541                     x = w;
8542                     y = 0;
8543                 break;
8544             }
8545             if(local === true){
8546                 return [x, y];
8547             }
8548             if(vp){
8549                 var sc = this.getScroll();
8550                 return [x + sc.left, y + sc.top];
8551             }
8552             //Add the element's offset xy
8553             var o = this.getXY();
8554             return [x+o[0], y+o[1]];
8555         },
8556
8557         /**
8558          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8559          * supported position values.
8560          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8561          * @param {String} position The position to align to.
8562          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8563          * @return {Array} [x, y]
8564          */
8565         getAlignToXY : function(el, p, o){
8566             el = Roo.get(el);
8567             var d = this.dom;
8568             if(!el.dom){
8569                 throw "Element.alignTo with an element that doesn't exist";
8570             }
8571             var c = false; //constrain to viewport
8572             var p1 = "", p2 = "";
8573             o = o || [0,0];
8574
8575             if(!p){
8576                 p = "tl-bl";
8577             }else if(p == "?"){
8578                 p = "tl-bl?";
8579             }else if(p.indexOf("-") == -1){
8580                 p = "tl-" + p;
8581             }
8582             p = p.toLowerCase();
8583             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8584             if(!m){
8585                throw "Element.alignTo with an invalid alignment " + p;
8586             }
8587             p1 = m[1]; p2 = m[2]; c = !!m[3];
8588
8589             //Subtract the aligned el's internal xy from the target's offset xy
8590             //plus custom offset to get the aligned el's new offset xy
8591             var a1 = this.getAnchorXY(p1, true);
8592             var a2 = el.getAnchorXY(p2, false);
8593             var x = a2[0] - a1[0] + o[0];
8594             var y = a2[1] - a1[1] + o[1];
8595             if(c){
8596                 //constrain the aligned el to viewport if necessary
8597                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8598                 // 5px of margin for ie
8599                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8600
8601                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8602                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8603                 //otherwise swap the aligned el to the opposite border of the target.
8604                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8605                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8606                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8607                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8608
8609                var doc = document;
8610                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8611                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8612
8613                if((x+w) > dw + scrollX){
8614                     x = swapX ? r.left-w : dw+scrollX-w;
8615                 }
8616                if(x < scrollX){
8617                    x = swapX ? r.right : scrollX;
8618                }
8619                if((y+h) > dh + scrollY){
8620                     y = swapY ? r.top-h : dh+scrollY-h;
8621                 }
8622                if (y < scrollY){
8623                    y = swapY ? r.bottom : scrollY;
8624                }
8625             }
8626             return [x,y];
8627         },
8628
8629         // private
8630         getConstrainToXY : function(){
8631             var os = {top:0, left:0, bottom:0, right: 0};
8632
8633             return function(el, local, offsets, proposedXY){
8634                 el = Roo.get(el);
8635                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8636
8637                 var vw, vh, vx = 0, vy = 0;
8638                 if(el.dom == document.body || el.dom == document){
8639                     vw = Roo.lib.Dom.getViewWidth();
8640                     vh = Roo.lib.Dom.getViewHeight();
8641                 }else{
8642                     vw = el.dom.clientWidth;
8643                     vh = el.dom.clientHeight;
8644                     if(!local){
8645                         var vxy = el.getXY();
8646                         vx = vxy[0];
8647                         vy = vxy[1];
8648                     }
8649                 }
8650
8651                 var s = el.getScroll();
8652
8653                 vx += offsets.left + s.left;
8654                 vy += offsets.top + s.top;
8655
8656                 vw -= offsets.right;
8657                 vh -= offsets.bottom;
8658
8659                 var vr = vx+vw;
8660                 var vb = vy+vh;
8661
8662                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8663                 var x = xy[0], y = xy[1];
8664                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8665
8666                 // only move it if it needs it
8667                 var moved = false;
8668
8669                 // first validate right/bottom
8670                 if((x + w) > vr){
8671                     x = vr - w;
8672                     moved = true;
8673                 }
8674                 if((y + h) > vb){
8675                     y = vb - h;
8676                     moved = true;
8677                 }
8678                 // then make sure top/left isn't negative
8679                 if(x < vx){
8680                     x = vx;
8681                     moved = true;
8682                 }
8683                 if(y < vy){
8684                     y = vy;
8685                     moved = true;
8686                 }
8687                 return moved ? [x, y] : false;
8688             };
8689         }(),
8690
8691         // private
8692         adjustForConstraints : function(xy, parent, offsets){
8693             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8694         },
8695
8696         /**
8697          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8698          * document it aligns it to the viewport.
8699          * The position parameter is optional, and can be specified in any one of the following formats:
8700          * <ul>
8701          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8702          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8703          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8704          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8705          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8706          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8707          * </ul>
8708          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8709          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8710          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8711          * that specified in order to enforce the viewport constraints.
8712          * Following are all of the supported anchor positions:
8713     <pre>
8714     Value  Description
8715     -----  -----------------------------
8716     tl     The top left corner (default)
8717     t      The center of the top edge
8718     tr     The top right corner
8719     l      The center of the left edge
8720     c      In the center of the element
8721     r      The center of the right edge
8722     bl     The bottom left corner
8723     b      The center of the bottom edge
8724     br     The bottom right corner
8725     </pre>
8726     Example Usage:
8727     <pre><code>
8728     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8729     el.alignTo("other-el");
8730
8731     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8732     el.alignTo("other-el", "tr?");
8733
8734     // align the bottom right corner of el with the center left edge of other-el
8735     el.alignTo("other-el", "br-l?");
8736
8737     // align the center of el with the bottom left corner of other-el and
8738     // adjust the x position by -6 pixels (and the y position by 0)
8739     el.alignTo("other-el", "c-bl", [-6, 0]);
8740     </code></pre>
8741          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8742          * @param {String} position The position to align to.
8743          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8744          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8745          * @return {Roo.Element} this
8746          */
8747         alignTo : function(element, position, offsets, animate){
8748             var xy = this.getAlignToXY(element, position, offsets);
8749             this.setXY(xy, this.preanim(arguments, 3));
8750             return this;
8751         },
8752
8753         /**
8754          * Anchors an element to another element and realigns it when the window is resized.
8755          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8756          * @param {String} position The position to align to.
8757          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8758          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8759          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8760          * is a number, it is used as the buffer delay (defaults to 50ms).
8761          * @param {Function} callback The function to call after the animation finishes
8762          * @return {Roo.Element} this
8763          */
8764         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8765             var action = function(){
8766                 this.alignTo(el, alignment, offsets, animate);
8767                 Roo.callback(callback, this);
8768             };
8769             Roo.EventManager.onWindowResize(action, this);
8770             var tm = typeof monitorScroll;
8771             if(tm != 'undefined'){
8772                 Roo.EventManager.on(window, 'scroll', action, this,
8773                     {buffer: tm == 'number' ? monitorScroll : 50});
8774             }
8775             action.call(this); // align immediately
8776             return this;
8777         },
8778         /**
8779          * Clears any opacity settings from this element. Required in some cases for IE.
8780          * @return {Roo.Element} this
8781          */
8782         clearOpacity : function(){
8783             if (window.ActiveXObject) {
8784                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8785                     this.dom.style.filter = "";
8786                 }
8787             } else {
8788                 this.dom.style.opacity = "";
8789                 this.dom.style["-moz-opacity"] = "";
8790                 this.dom.style["-khtml-opacity"] = "";
8791             }
8792             return this;
8793         },
8794
8795         /**
8796          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8797          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8798          * @return {Roo.Element} this
8799          */
8800         hide : function(animate){
8801             this.setVisible(false, this.preanim(arguments, 0));
8802             return this;
8803         },
8804
8805         /**
8806         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8807         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8808          * @return {Roo.Element} this
8809          */
8810         show : function(animate){
8811             this.setVisible(true, this.preanim(arguments, 0));
8812             return this;
8813         },
8814
8815         /**
8816          * @private Test if size has a unit, otherwise appends the default
8817          */
8818         addUnits : function(size){
8819             return Roo.Element.addUnits(size, this.defaultUnit);
8820         },
8821
8822         /**
8823          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8824          * @return {Roo.Element} this
8825          */
8826         beginMeasure : function(){
8827             var el = this.dom;
8828             if(el.offsetWidth || el.offsetHeight){
8829                 return this; // offsets work already
8830             }
8831             var changed = [];
8832             var p = this.dom, b = document.body; // start with this element
8833             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8834                 var pe = Roo.get(p);
8835                 if(pe.getStyle('display') == 'none'){
8836                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8837                     p.style.visibility = "hidden";
8838                     p.style.display = "block";
8839                 }
8840                 p = p.parentNode;
8841             }
8842             this._measureChanged = changed;
8843             return this;
8844
8845         },
8846
8847         /**
8848          * Restores displays to before beginMeasure was called
8849          * @return {Roo.Element} this
8850          */
8851         endMeasure : function(){
8852             var changed = this._measureChanged;
8853             if(changed){
8854                 for(var i = 0, len = changed.length; i < len; i++) {
8855                     var r = changed[i];
8856                     r.el.style.visibility = r.visibility;
8857                     r.el.style.display = "none";
8858                 }
8859                 this._measureChanged = null;
8860             }
8861             return this;
8862         },
8863
8864         /**
8865         * Update the innerHTML of this element, optionally searching for and processing scripts
8866         * @param {String} html The new HTML
8867         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8868         * @param {Function} callback For async script loading you can be noticed when the update completes
8869         * @return {Roo.Element} this
8870          */
8871         update : function(html, loadScripts, callback){
8872             if(typeof html == "undefined"){
8873                 html = "";
8874             }
8875             if(loadScripts !== true){
8876                 this.dom.innerHTML = html;
8877                 if(typeof callback == "function"){
8878                     callback();
8879                 }
8880                 return this;
8881             }
8882             var id = Roo.id();
8883             var dom = this.dom;
8884
8885             html += '<span id="' + id + '"></span>';
8886
8887             E.onAvailable(id, function(){
8888                 var hd = document.getElementsByTagName("head")[0];
8889                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8890                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8891                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8892
8893                 var match;
8894                 while(match = re.exec(html)){
8895                     var attrs = match[1];
8896                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8897                     if(srcMatch && srcMatch[2]){
8898                        var s = document.createElement("script");
8899                        s.src = srcMatch[2];
8900                        var typeMatch = attrs.match(typeRe);
8901                        if(typeMatch && typeMatch[2]){
8902                            s.type = typeMatch[2];
8903                        }
8904                        hd.appendChild(s);
8905                     }else if(match[2] && match[2].length > 0){
8906                         if(window.execScript) {
8907                            window.execScript(match[2]);
8908                         } else {
8909                             /**
8910                              * eval:var:id
8911                              * eval:var:dom
8912                              * eval:var:html
8913                              * 
8914                              */
8915                            window.eval(match[2]);
8916                         }
8917                     }
8918                 }
8919                 var el = document.getElementById(id);
8920                 if(el){el.parentNode.removeChild(el);}
8921                 if(typeof callback == "function"){
8922                     callback();
8923                 }
8924             });
8925             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8926             return this;
8927         },
8928
8929         /**
8930          * Direct access to the UpdateManager update() method (takes the same parameters).
8931          * @param {String/Function} url The url for this request or a function to call to get the url
8932          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8933          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8934          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8935          * @return {Roo.Element} this
8936          */
8937         load : function(){
8938             var um = this.getUpdateManager();
8939             um.update.apply(um, arguments);
8940             return this;
8941         },
8942
8943         /**
8944         * Gets this element's UpdateManager
8945         * @return {Roo.UpdateManager} The UpdateManager
8946         */
8947         getUpdateManager : function(){
8948             if(!this.updateManager){
8949                 this.updateManager = new Roo.UpdateManager(this);
8950             }
8951             return this.updateManager;
8952         },
8953
8954         /**
8955          * Disables text selection for this element (normalized across browsers)
8956          * @return {Roo.Element} this
8957          */
8958         unselectable : function(){
8959             this.dom.unselectable = "on";
8960             this.swallowEvent("selectstart", true);
8961             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8962             this.addClass("x-unselectable");
8963             return this;
8964         },
8965
8966         /**
8967         * Calculates the x, y to center this element on the screen
8968         * @return {Array} The x, y values [x, y]
8969         */
8970         getCenterXY : function(){
8971             return this.getAlignToXY(document, 'c-c');
8972         },
8973
8974         /**
8975         * Centers the Element in either the viewport, or another Element.
8976         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8977         */
8978         center : function(centerIn){
8979             this.alignTo(centerIn || document, 'c-c');
8980             return this;
8981         },
8982
8983         /**
8984          * Tests various css rules/browsers to determine if this element uses a border box
8985          * @return {Boolean}
8986          */
8987         isBorderBox : function(){
8988             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8989         },
8990
8991         /**
8992          * Return a box {x, y, width, height} that can be used to set another elements
8993          * size/location to match this element.
8994          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8995          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8996          * @return {Object} box An object in the format {x, y, width, height}
8997          */
8998         getBox : function(contentBox, local){
8999             var xy;
9000             if(!local){
9001                 xy = this.getXY();
9002             }else{
9003                 var left = parseInt(this.getStyle("left"), 10) || 0;
9004                 var top = parseInt(this.getStyle("top"), 10) || 0;
9005                 xy = [left, top];
9006             }
9007             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9008             if(!contentBox){
9009                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9010             }else{
9011                 var l = this.getBorderWidth("l")+this.getPadding("l");
9012                 var r = this.getBorderWidth("r")+this.getPadding("r");
9013                 var t = this.getBorderWidth("t")+this.getPadding("t");
9014                 var b = this.getBorderWidth("b")+this.getPadding("b");
9015                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
9016             }
9017             bx.right = bx.x + bx.width;
9018             bx.bottom = bx.y + bx.height;
9019             return bx;
9020         },
9021
9022         /**
9023          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9024          for more information about the sides.
9025          * @param {String} sides
9026          * @return {Number}
9027          */
9028         getFrameWidth : function(sides, onlyContentBox){
9029             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9030         },
9031
9032         /**
9033          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
9034          * @param {Object} box The box to fill {x, y, width, height}
9035          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9036          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9037          * @return {Roo.Element} this
9038          */
9039         setBox : function(box, adjust, animate){
9040             var w = box.width, h = box.height;
9041             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9042                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9043                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9044             }
9045             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9046             return this;
9047         },
9048
9049         /**
9050          * Forces the browser to repaint this element
9051          * @return {Roo.Element} this
9052          */
9053          repaint : function(){
9054             var dom = this.dom;
9055             this.addClass("x-repaint");
9056             setTimeout(function(){
9057                 Roo.get(dom).removeClass("x-repaint");
9058             }, 1);
9059             return this;
9060         },
9061
9062         /**
9063          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9064          * then it returns the calculated width of the sides (see getPadding)
9065          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9066          * @return {Object/Number}
9067          */
9068         getMargins : function(side){
9069             if(!side){
9070                 return {
9071                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9072                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9073                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9074                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9075                 };
9076             }else{
9077                 return this.addStyles(side, El.margins);
9078              }
9079         },
9080
9081         // private
9082         addStyles : function(sides, styles){
9083             var val = 0, v, w;
9084             for(var i = 0, len = sides.length; i < len; i++){
9085                 v = this.getStyle(styles[sides.charAt(i)]);
9086                 if(v){
9087                      w = parseInt(v, 10);
9088                      if(w){ val += w; }
9089                 }
9090             }
9091             return val;
9092         },
9093
9094         /**
9095          * Creates a proxy element of this element
9096          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9097          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9098          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9099          * @return {Roo.Element} The new proxy element
9100          */
9101         createProxy : function(config, renderTo, matchBox){
9102             if(renderTo){
9103                 renderTo = Roo.getDom(renderTo);
9104             }else{
9105                 renderTo = document.body;
9106             }
9107             config = typeof config == "object" ?
9108                 config : {tag : "div", cls: config};
9109             var proxy = Roo.DomHelper.append(renderTo, config, true);
9110             if(matchBox){
9111                proxy.setBox(this.getBox());
9112             }
9113             return proxy;
9114         },
9115
9116         /**
9117          * Puts a mask over this element to disable user interaction. Requires core.css.
9118          * This method can only be applied to elements which accept child nodes.
9119          * @param {String} msg (optional) A message to display in the mask
9120          * @param {String} msgCls (optional) A css class to apply to the msg element
9121          * @return {Element} The mask  element
9122          */
9123         mask : function(msg, msgCls)
9124         {
9125             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9126                 this.setStyle("position", "relative");
9127             }
9128             if(!this._mask){
9129                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9130             }
9131             
9132             this.addClass("x-masked");
9133             this._mask.setDisplayed(true);
9134             
9135             // we wander
9136             var z = 0;
9137             var dom = this.dom;
9138             while (dom && dom.style) {
9139                 if (!isNaN(parseInt(dom.style.zIndex))) {
9140                     z = Math.max(z, parseInt(dom.style.zIndex));
9141                 }
9142                 dom = dom.parentNode;
9143             }
9144             // if we are masking the body - then it hides everything..
9145             if (this.dom == document.body) {
9146                 z = 1000000;
9147                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9148                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9149             }
9150            
9151             if(typeof msg == 'string'){
9152                 if(!this._maskMsg){
9153                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9154                         cls: "roo-el-mask-msg", 
9155                         cn: [
9156                             {
9157                                 tag: 'i',
9158                                 cls: 'fa fa-spinner fa-spin'
9159                             },
9160                             {
9161                                 tag: 'div'
9162                             }   
9163                         ]
9164                     }, true);
9165                 }
9166                 var mm = this._maskMsg;
9167                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9168                 if (mm.dom.lastChild) { // weird IE issue?
9169                     mm.dom.lastChild.innerHTML = msg;
9170                 }
9171                 mm.setDisplayed(true);
9172                 mm.center(this);
9173                 mm.setStyle('z-index', z + 102);
9174             }
9175             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9176                 this._mask.setHeight(this.getHeight());
9177             }
9178             this._mask.setStyle('z-index', z + 100);
9179             
9180             return this._mask;
9181         },
9182
9183         /**
9184          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9185          * it is cached for reuse.
9186          */
9187         unmask : function(removeEl){
9188             if(this._mask){
9189                 if(removeEl === true){
9190                     this._mask.remove();
9191                     delete this._mask;
9192                     if(this._maskMsg){
9193                         this._maskMsg.remove();
9194                         delete this._maskMsg;
9195                     }
9196                 }else{
9197                     this._mask.setDisplayed(false);
9198                     if(this._maskMsg){
9199                         this._maskMsg.setDisplayed(false);
9200                     }
9201                 }
9202             }
9203             this.removeClass("x-masked");
9204         },
9205
9206         /**
9207          * Returns true if this element is masked
9208          * @return {Boolean}
9209          */
9210         isMasked : function(){
9211             return this._mask && this._mask.isVisible();
9212         },
9213
9214         /**
9215          * Creates an iframe shim for this element to keep selects and other windowed objects from
9216          * showing through.
9217          * @return {Roo.Element} The new shim element
9218          */
9219         createShim : function(){
9220             var el = document.createElement('iframe');
9221             el.frameBorder = 'no';
9222             el.className = 'roo-shim';
9223             if(Roo.isIE && Roo.isSecure){
9224                 el.src = Roo.SSL_SECURE_URL;
9225             }
9226             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9227             shim.autoBoxAdjust = false;
9228             return shim;
9229         },
9230
9231         /**
9232          * Removes this element from the DOM and deletes it from the cache
9233          */
9234         remove : function(){
9235             if(this.dom.parentNode){
9236                 this.dom.parentNode.removeChild(this.dom);
9237             }
9238             delete El.cache[this.dom.id];
9239         },
9240
9241         /**
9242          * Sets up event handlers to add and remove a css class when the mouse is over this element
9243          * @param {String} className
9244          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9245          * mouseout events for children elements
9246          * @return {Roo.Element} this
9247          */
9248         addClassOnOver : function(className, preventFlicker){
9249             this.on("mouseover", function(){
9250                 Roo.fly(this, '_internal').addClass(className);
9251             }, this.dom);
9252             var removeFn = function(e){
9253                 if(preventFlicker !== true || !e.within(this, true)){
9254                     Roo.fly(this, '_internal').removeClass(className);
9255                 }
9256             };
9257             this.on("mouseout", removeFn, this.dom);
9258             return this;
9259         },
9260
9261         /**
9262          * Sets up event handlers to add and remove a css class when this element has the focus
9263          * @param {String} className
9264          * @return {Roo.Element} this
9265          */
9266         addClassOnFocus : function(className){
9267             this.on("focus", function(){
9268                 Roo.fly(this, '_internal').addClass(className);
9269             }, this.dom);
9270             this.on("blur", function(){
9271                 Roo.fly(this, '_internal').removeClass(className);
9272             }, this.dom);
9273             return this;
9274         },
9275         /**
9276          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9277          * @param {String} className
9278          * @return {Roo.Element} this
9279          */
9280         addClassOnClick : function(className){
9281             var dom = this.dom;
9282             this.on("mousedown", function(){
9283                 Roo.fly(dom, '_internal').addClass(className);
9284                 var d = Roo.get(document);
9285                 var fn = function(){
9286                     Roo.fly(dom, '_internal').removeClass(className);
9287                     d.removeListener("mouseup", fn);
9288                 };
9289                 d.on("mouseup", fn);
9290             });
9291             return this;
9292         },
9293
9294         /**
9295          * Stops the specified event from bubbling and optionally prevents the default action
9296          * @param {String} eventName
9297          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9298          * @return {Roo.Element} this
9299          */
9300         swallowEvent : function(eventName, preventDefault){
9301             var fn = function(e){
9302                 e.stopPropagation();
9303                 if(preventDefault){
9304                     e.preventDefault();
9305                 }
9306             };
9307             if(eventName instanceof Array){
9308                 for(var i = 0, len = eventName.length; i < len; i++){
9309                      this.on(eventName[i], fn);
9310                 }
9311                 return this;
9312             }
9313             this.on(eventName, fn);
9314             return this;
9315         },
9316
9317         /**
9318          * @private
9319          */
9320       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9321
9322         /**
9323          * Sizes this element to its parent element's dimensions performing
9324          * neccessary box adjustments.
9325          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9326          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9327          * @return {Roo.Element} this
9328          */
9329         fitToParent : function(monitorResize, targetParent) {
9330           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9331           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9332           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9333             return;
9334           }
9335           var p = Roo.get(targetParent || this.dom.parentNode);
9336           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9337           if (monitorResize === true) {
9338             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9339             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9340           }
9341           return this;
9342         },
9343
9344         /**
9345          * Gets the next sibling, skipping text nodes
9346          * @return {HTMLElement} The next sibling or null
9347          */
9348         getNextSibling : function(){
9349             var n = this.dom.nextSibling;
9350             while(n && n.nodeType != 1){
9351                 n = n.nextSibling;
9352             }
9353             return n;
9354         },
9355
9356         /**
9357          * Gets the previous sibling, skipping text nodes
9358          * @return {HTMLElement} The previous sibling or null
9359          */
9360         getPrevSibling : function(){
9361             var n = this.dom.previousSibling;
9362             while(n && n.nodeType != 1){
9363                 n = n.previousSibling;
9364             }
9365             return n;
9366         },
9367
9368
9369         /**
9370          * Appends the passed element(s) to this element
9371          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9372          * @return {Roo.Element} this
9373          */
9374         appendChild: function(el){
9375             el = Roo.get(el);
9376             el.appendTo(this);
9377             return this;
9378         },
9379
9380         /**
9381          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9382          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9383          * automatically generated with the specified attributes.
9384          * @param {HTMLElement} insertBefore (optional) a child element of this element
9385          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9386          * @return {Roo.Element} The new child element
9387          */
9388         createChild: function(config, insertBefore, returnDom){
9389             config = config || {tag:'div'};
9390             if(insertBefore){
9391                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9392             }
9393             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9394         },
9395
9396         /**
9397          * Appends this element to the passed element
9398          * @param {String/HTMLElement/Element} el The new parent element
9399          * @return {Roo.Element} this
9400          */
9401         appendTo: function(el){
9402             el = Roo.getDom(el);
9403             el.appendChild(this.dom);
9404             return this;
9405         },
9406
9407         /**
9408          * Inserts this element before the passed element in the DOM
9409          * @param {String/HTMLElement/Element} el The element to insert before
9410          * @return {Roo.Element} this
9411          */
9412         insertBefore: function(el){
9413             el = Roo.getDom(el);
9414             el.parentNode.insertBefore(this.dom, el);
9415             return this;
9416         },
9417
9418         /**
9419          * Inserts this element after the passed element in the DOM
9420          * @param {String/HTMLElement/Element} el The element to insert after
9421          * @return {Roo.Element} this
9422          */
9423         insertAfter: function(el){
9424             el = Roo.getDom(el);
9425             el.parentNode.insertBefore(this.dom, el.nextSibling);
9426             return this;
9427         },
9428
9429         /**
9430          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9431          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9432          * @return {Roo.Element} The new child
9433          */
9434         insertFirst: function(el, returnDom){
9435             el = el || {};
9436             if(typeof el == 'object' && !el.nodeType){ // dh config
9437                 return this.createChild(el, this.dom.firstChild, returnDom);
9438             }else{
9439                 el = Roo.getDom(el);
9440                 this.dom.insertBefore(el, this.dom.firstChild);
9441                 return !returnDom ? Roo.get(el) : el;
9442             }
9443         },
9444
9445         /**
9446          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9447          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9448          * @param {String} where (optional) 'before' or 'after' defaults to before
9449          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9450          * @return {Roo.Element} the inserted Element
9451          */
9452         insertSibling: function(el, where, returnDom){
9453             where = where ? where.toLowerCase() : 'before';
9454             el = el || {};
9455             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9456
9457             if(typeof el == 'object' && !el.nodeType){ // dh config
9458                 if(where == 'after' && !this.dom.nextSibling){
9459                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9460                 }else{
9461                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9462                 }
9463
9464             }else{
9465                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9466                             where == 'before' ? this.dom : this.dom.nextSibling);
9467                 if(!returnDom){
9468                     rt = Roo.get(rt);
9469                 }
9470             }
9471             return rt;
9472         },
9473
9474         /**
9475          * Creates and wraps this element with another element
9476          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9477          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9478          * @return {HTMLElement/Element} The newly created wrapper element
9479          */
9480         wrap: function(config, returnDom){
9481             if(!config){
9482                 config = {tag: "div"};
9483             }
9484             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9485             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9486             return newEl;
9487         },
9488
9489         /**
9490          * Replaces the passed element with this element
9491          * @param {String/HTMLElement/Element} el The element to replace
9492          * @return {Roo.Element} this
9493          */
9494         replace: function(el){
9495             el = Roo.get(el);
9496             this.insertBefore(el);
9497             el.remove();
9498             return this;
9499         },
9500
9501         /**
9502          * Inserts an html fragment into this element
9503          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9504          * @param {String} html The HTML fragment
9505          * @param {Boolean} returnEl True to return an Roo.Element
9506          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9507          */
9508         insertHtml : function(where, html, returnEl){
9509             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9510             return returnEl ? Roo.get(el) : el;
9511         },
9512
9513         /**
9514          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9515          * @param {Object} o The object with the attributes
9516          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9517          * @return {Roo.Element} this
9518          */
9519         set : function(o, useSet){
9520             var el = this.dom;
9521             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9522             for(var attr in o){
9523                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9524                 if(attr=="cls"){
9525                     el.className = o["cls"];
9526                 }else{
9527                     if(useSet) {
9528                         el.setAttribute(attr, o[attr]);
9529                     } else {
9530                         el[attr] = o[attr];
9531                     }
9532                 }
9533             }
9534             if(o.style){
9535                 Roo.DomHelper.applyStyles(el, o.style);
9536             }
9537             return this;
9538         },
9539
9540         /**
9541          * Convenience method for constructing a KeyMap
9542          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9543          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9544          * @param {Function} fn The function to call
9545          * @param {Object} scope (optional) The scope of the function
9546          * @return {Roo.KeyMap} The KeyMap created
9547          */
9548         addKeyListener : function(key, fn, scope){
9549             var config;
9550             if(typeof key != "object" || key instanceof Array){
9551                 config = {
9552                     key: key,
9553                     fn: fn,
9554                     scope: scope
9555                 };
9556             }else{
9557                 config = {
9558                     key : key.key,
9559                     shift : key.shift,
9560                     ctrl : key.ctrl,
9561                     alt : key.alt,
9562                     fn: fn,
9563                     scope: scope
9564                 };
9565             }
9566             return new Roo.KeyMap(this, config);
9567         },
9568
9569         /**
9570          * Creates a KeyMap for this element
9571          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9572          * @return {Roo.KeyMap} The KeyMap created
9573          */
9574         addKeyMap : function(config){
9575             return new Roo.KeyMap(this, config);
9576         },
9577
9578         /**
9579          * Returns true if this element is scrollable.
9580          * @return {Boolean}
9581          */
9582          isScrollable : function(){
9583             var dom = this.dom;
9584             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9585         },
9586
9587         /**
9588          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9589          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9590          * @param {Number} value The new scroll value
9591          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9592          * @return {Element} this
9593          */
9594
9595         scrollTo : function(side, value, animate){
9596             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9597             if(!animate || !A){
9598                 this.dom[prop] = value;
9599             }else{
9600                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9601                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9602             }
9603             return this;
9604         },
9605
9606         /**
9607          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9608          * within this element's scrollable range.
9609          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9610          * @param {Number} distance How far to scroll the element in pixels
9611          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9612          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9613          * was scrolled as far as it could go.
9614          */
9615          scroll : function(direction, distance, animate){
9616              if(!this.isScrollable()){
9617                  return;
9618              }
9619              var el = this.dom;
9620              var l = el.scrollLeft, t = el.scrollTop;
9621              var w = el.scrollWidth, h = el.scrollHeight;
9622              var cw = el.clientWidth, ch = el.clientHeight;
9623              direction = direction.toLowerCase();
9624              var scrolled = false;
9625              var a = this.preanim(arguments, 2);
9626              switch(direction){
9627                  case "l":
9628                  case "left":
9629                      if(w - l > cw){
9630                          var v = Math.min(l + distance, w-cw);
9631                          this.scrollTo("left", v, a);
9632                          scrolled = true;
9633                      }
9634                      break;
9635                 case "r":
9636                 case "right":
9637                      if(l > 0){
9638                          var v = Math.max(l - distance, 0);
9639                          this.scrollTo("left", v, a);
9640                          scrolled = true;
9641                      }
9642                      break;
9643                 case "t":
9644                 case "top":
9645                 case "up":
9646                      if(t > 0){
9647                          var v = Math.max(t - distance, 0);
9648                          this.scrollTo("top", v, a);
9649                          scrolled = true;
9650                      }
9651                      break;
9652                 case "b":
9653                 case "bottom":
9654                 case "down":
9655                      if(h - t > ch){
9656                          var v = Math.min(t + distance, h-ch);
9657                          this.scrollTo("top", v, a);
9658                          scrolled = true;
9659                      }
9660                      break;
9661              }
9662              return scrolled;
9663         },
9664
9665         /**
9666          * Translates the passed page coordinates into left/top css values for this element
9667          * @param {Number/Array} x The page x or an array containing [x, y]
9668          * @param {Number} y The page y
9669          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9670          */
9671         translatePoints : function(x, y){
9672             if(typeof x == 'object' || x instanceof Array){
9673                 y = x[1]; x = x[0];
9674             }
9675             var p = this.getStyle('position');
9676             var o = this.getXY();
9677
9678             var l = parseInt(this.getStyle('left'), 10);
9679             var t = parseInt(this.getStyle('top'), 10);
9680
9681             if(isNaN(l)){
9682                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9683             }
9684             if(isNaN(t)){
9685                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9686             }
9687
9688             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9689         },
9690
9691         /**
9692          * Returns the current scroll position of the element.
9693          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9694          */
9695         getScroll : function(){
9696             var d = this.dom, doc = document;
9697             if(d == doc || d == doc.body){
9698                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9699                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9700                 return {left: l, top: t};
9701             }else{
9702                 return {left: d.scrollLeft, top: d.scrollTop};
9703             }
9704         },
9705
9706         /**
9707          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9708          * are convert to standard 6 digit hex color.
9709          * @param {String} attr The css attribute
9710          * @param {String} defaultValue The default value to use when a valid color isn't found
9711          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9712          * YUI color anims.
9713          */
9714         getColor : function(attr, defaultValue, prefix){
9715             var v = this.getStyle(attr);
9716             if(!v || v == "transparent" || v == "inherit") {
9717                 return defaultValue;
9718             }
9719             var color = typeof prefix == "undefined" ? "#" : prefix;
9720             if(v.substr(0, 4) == "rgb("){
9721                 var rvs = v.slice(4, v.length -1).split(",");
9722                 for(var i = 0; i < 3; i++){
9723                     var h = parseInt(rvs[i]).toString(16);
9724                     if(h < 16){
9725                         h = "0" + h;
9726                     }
9727                     color += h;
9728                 }
9729             } else {
9730                 if(v.substr(0, 1) == "#"){
9731                     if(v.length == 4) {
9732                         for(var i = 1; i < 4; i++){
9733                             var c = v.charAt(i);
9734                             color +=  c + c;
9735                         }
9736                     }else if(v.length == 7){
9737                         color += v.substr(1);
9738                     }
9739                 }
9740             }
9741             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9742         },
9743
9744         /**
9745          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9746          * gradient background, rounded corners and a 4-way shadow.
9747          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9748          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9749          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9750          * @return {Roo.Element} this
9751          */
9752         boxWrap : function(cls){
9753             cls = cls || 'x-box';
9754             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9755             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9756             return el;
9757         },
9758
9759         /**
9760          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9761          * @param {String} namespace The namespace in which to look for the attribute
9762          * @param {String} name The attribute name
9763          * @return {String} The attribute value
9764          */
9765         getAttributeNS : Roo.isIE ? function(ns, name){
9766             var d = this.dom;
9767             var type = typeof d[ns+":"+name];
9768             if(type != 'undefined' && type != 'unknown'){
9769                 return d[ns+":"+name];
9770             }
9771             return d[name];
9772         } : function(ns, name){
9773             var d = this.dom;
9774             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9775         },
9776         
9777         
9778         /**
9779          * Sets or Returns the value the dom attribute value
9780          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9781          * @param {String} value (optional) The value to set the attribute to
9782          * @return {String} The attribute value
9783          */
9784         attr : function(name){
9785             if (arguments.length > 1) {
9786                 this.dom.setAttribute(name, arguments[1]);
9787                 return arguments[1];
9788             }
9789             if (typeof(name) == 'object') {
9790                 for(var i in name) {
9791                     this.attr(i, name[i]);
9792                 }
9793                 return name;
9794             }
9795             
9796             
9797             if (!this.dom.hasAttribute(name)) {
9798                 return undefined;
9799             }
9800             return this.dom.getAttribute(name);
9801         }
9802         
9803         
9804         
9805     };
9806
9807     var ep = El.prototype;
9808
9809     /**
9810      * Appends an event handler (Shorthand for addListener)
9811      * @param {String}   eventName     The type of event to append
9812      * @param {Function} fn        The method the event invokes
9813      * @param {Object} scope       (optional) The scope (this object) of the fn
9814      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9815      * @method
9816      */
9817     ep.on = ep.addListener;
9818         // backwards compat
9819     ep.mon = ep.addListener;
9820
9821     /**
9822      * Removes an event handler from this element (shorthand for removeListener)
9823      * @param {String} eventName the type of event to remove
9824      * @param {Function} fn the method the event invokes
9825      * @return {Roo.Element} this
9826      * @method
9827      */
9828     ep.un = ep.removeListener;
9829
9830     /**
9831      * true to automatically adjust width and height settings for box-model issues (default to true)
9832      */
9833     ep.autoBoxAdjust = true;
9834
9835     // private
9836     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9837
9838     // private
9839     El.addUnits = function(v, defaultUnit){
9840         if(v === "" || v == "auto"){
9841             return v;
9842         }
9843         if(v === undefined){
9844             return '';
9845         }
9846         if(typeof v == "number" || !El.unitPattern.test(v)){
9847             return v + (defaultUnit || 'px');
9848         }
9849         return v;
9850     };
9851
9852     // special markup used throughout Roo when box wrapping elements
9853     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9854     /**
9855      * Visibility mode constant - Use visibility to hide element
9856      * @static
9857      * @type Number
9858      */
9859     El.VISIBILITY = 1;
9860     /**
9861      * Visibility mode constant - Use display to hide element
9862      * @static
9863      * @type Number
9864      */
9865     El.DISPLAY = 2;
9866
9867     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9868     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9869     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9870
9871
9872
9873     /**
9874      * @private
9875      */
9876     El.cache = {};
9877
9878     var docEl;
9879
9880     /**
9881      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9882      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9883      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9884      * @return {Element} The Element object
9885      * @static
9886      */
9887     El.get = function(el){
9888         var ex, elm, id;
9889         if(!el){ return null; }
9890         if(typeof el == "string"){ // element id
9891             if(!(elm = document.getElementById(el))){
9892                 return null;
9893             }
9894             if(ex = El.cache[el]){
9895                 ex.dom = elm;
9896             }else{
9897                 ex = El.cache[el] = new El(elm);
9898             }
9899             return ex;
9900         }else if(el.tagName){ // dom element
9901             if(!(id = el.id)){
9902                 id = Roo.id(el);
9903             }
9904             if(ex = El.cache[id]){
9905                 ex.dom = el;
9906             }else{
9907                 ex = El.cache[id] = new El(el);
9908             }
9909             return ex;
9910         }else if(el instanceof El){
9911             if(el != docEl){
9912                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9913                                                               // catch case where it hasn't been appended
9914                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9915             }
9916             return el;
9917         }else if(el.isComposite){
9918             return el;
9919         }else if(el instanceof Array){
9920             return El.select(el);
9921         }else if(el == document){
9922             // create a bogus element object representing the document object
9923             if(!docEl){
9924                 var f = function(){};
9925                 f.prototype = El.prototype;
9926                 docEl = new f();
9927                 docEl.dom = document;
9928             }
9929             return docEl;
9930         }
9931         return null;
9932     };
9933
9934     // private
9935     El.uncache = function(el){
9936         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9937             if(a[i]){
9938                 delete El.cache[a[i].id || a[i]];
9939             }
9940         }
9941     };
9942
9943     // private
9944     // Garbage collection - uncache elements/purge listeners on orphaned elements
9945     // so we don't hold a reference and cause the browser to retain them
9946     El.garbageCollect = function(){
9947         if(!Roo.enableGarbageCollector){
9948             clearInterval(El.collectorThread);
9949             return;
9950         }
9951         for(var eid in El.cache){
9952             var el = El.cache[eid], d = el.dom;
9953             // -------------------------------------------------------
9954             // Determining what is garbage:
9955             // -------------------------------------------------------
9956             // !d
9957             // dom node is null, definitely garbage
9958             // -------------------------------------------------------
9959             // !d.parentNode
9960             // no parentNode == direct orphan, definitely garbage
9961             // -------------------------------------------------------
9962             // !d.offsetParent && !document.getElementById(eid)
9963             // display none elements have no offsetParent so we will
9964             // also try to look it up by it's id. However, check
9965             // offsetParent first so we don't do unneeded lookups.
9966             // This enables collection of elements that are not orphans
9967             // directly, but somewhere up the line they have an orphan
9968             // parent.
9969             // -------------------------------------------------------
9970             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9971                 delete El.cache[eid];
9972                 if(d && Roo.enableListenerCollection){
9973                     E.purgeElement(d);
9974                 }
9975             }
9976         }
9977     }
9978     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9979
9980
9981     // dom is optional
9982     El.Flyweight = function(dom){
9983         this.dom = dom;
9984     };
9985     El.Flyweight.prototype = El.prototype;
9986
9987     El._flyweights = {};
9988     /**
9989      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9990      * the dom node can be overwritten by other code.
9991      * @param {String/HTMLElement} el The dom node or id
9992      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9993      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9994      * @static
9995      * @return {Element} The shared Element object
9996      */
9997     El.fly = function(el, named){
9998         named = named || '_global';
9999         el = Roo.getDom(el);
10000         if(!el){
10001             return null;
10002         }
10003         if(!El._flyweights[named]){
10004             El._flyweights[named] = new El.Flyweight();
10005         }
10006         El._flyweights[named].dom = el;
10007         return El._flyweights[named];
10008     };
10009
10010     /**
10011      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10012      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10013      * Shorthand of {@link Roo.Element#get}
10014      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10015      * @return {Element} The Element object
10016      * @member Roo
10017      * @method get
10018      */
10019     Roo.get = El.get;
10020     /**
10021      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10022      * the dom node can be overwritten by other code.
10023      * Shorthand of {@link Roo.Element#fly}
10024      * @param {String/HTMLElement} el The dom node or id
10025      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10026      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10027      * @static
10028      * @return {Element} The shared Element object
10029      * @member Roo
10030      * @method fly
10031      */
10032     Roo.fly = El.fly;
10033
10034     // speedy lookup for elements never to box adjust
10035     var noBoxAdjust = Roo.isStrict ? {
10036         select:1
10037     } : {
10038         input:1, select:1, textarea:1
10039     };
10040     if(Roo.isIE || Roo.isGecko){
10041         noBoxAdjust['button'] = 1;
10042     }
10043
10044
10045     Roo.EventManager.on(window, 'unload', function(){
10046         delete El.cache;
10047         delete El._flyweights;
10048     });
10049 })();
10050
10051
10052
10053
10054 if(Roo.DomQuery){
10055     Roo.Element.selectorFunction = Roo.DomQuery.select;
10056 }
10057
10058 Roo.Element.select = function(selector, unique, root){
10059     var els;
10060     if(typeof selector == "string"){
10061         els = Roo.Element.selectorFunction(selector, root);
10062     }else if(selector.length !== undefined){
10063         els = selector;
10064     }else{
10065         throw "Invalid selector";
10066     }
10067     if(unique === true){
10068         return new Roo.CompositeElement(els);
10069     }else{
10070         return new Roo.CompositeElementLite(els);
10071     }
10072 };
10073 /**
10074  * Selects elements based on the passed CSS selector to enable working on them as 1.
10075  * @param {String/Array} selector The CSS selector or an array of elements
10076  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10077  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10078  * @return {CompositeElementLite/CompositeElement}
10079  * @member Roo
10080  * @method select
10081  */
10082 Roo.select = Roo.Element.select;
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095
10096
10097 /*
10098  * Based on:
10099  * Ext JS Library 1.1.1
10100  * Copyright(c) 2006-2007, Ext JS, LLC.
10101  *
10102  * Originally Released Under LGPL - original licence link has changed is not relivant.
10103  *
10104  * Fork - LGPL
10105  * <script type="text/javascript">
10106  */
10107
10108
10109
10110 //Notifies Element that fx methods are available
10111 Roo.enableFx = true;
10112
10113 /**
10114  * @class Roo.Fx
10115  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10116  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10117  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10118  * Element effects to work.</p><br/>
10119  *
10120  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10121  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10122  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10123  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10124  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10125  * expected results and should be done with care.</p><br/>
10126  *
10127  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10128  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10129 <pre>
10130 Value  Description
10131 -----  -----------------------------
10132 tl     The top left corner
10133 t      The center of the top edge
10134 tr     The top right corner
10135 l      The center of the left edge
10136 r      The center of the right edge
10137 bl     The bottom left corner
10138 b      The center of the bottom edge
10139 br     The bottom right corner
10140 </pre>
10141  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10142  * below are common options that can be passed to any Fx method.</b>
10143  * @cfg {Function} callback A function called when the effect is finished
10144  * @cfg {Object} scope The scope of the effect function
10145  * @cfg {String} easing A valid Easing value for the effect
10146  * @cfg {String} afterCls A css class to apply after the effect
10147  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10148  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10149  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10150  * effects that end with the element being visually hidden, ignored otherwise)
10151  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10152  * a function which returns such a specification that will be applied to the Element after the effect finishes
10153  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10154  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10155  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10156  */
10157 Roo.Fx = {
10158         /**
10159          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10160          * origin for the slide effect.  This function automatically handles wrapping the element with
10161          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10162          * Usage:
10163          *<pre><code>
10164 // default: slide the element in from the top
10165 el.slideIn();
10166
10167 // custom: slide the element in from the right with a 2-second duration
10168 el.slideIn('r', { duration: 2 });
10169
10170 // common config options shown with default values
10171 el.slideIn('t', {
10172     easing: 'easeOut',
10173     duration: .5
10174 });
10175 </code></pre>
10176          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10177          * @param {Object} options (optional) Object literal with any of the Fx config options
10178          * @return {Roo.Element} The Element
10179          */
10180     slideIn : function(anchor, o){
10181         var el = this.getFxEl();
10182         o = o || {};
10183
10184         el.queueFx(o, function(){
10185
10186             anchor = anchor || "t";
10187
10188             // fix display to visibility
10189             this.fixDisplay();
10190
10191             // restore values after effect
10192             var r = this.getFxRestore();
10193             var b = this.getBox();
10194             // fixed size for slide
10195             this.setSize(b);
10196
10197             // wrap if needed
10198             var wrap = this.fxWrap(r.pos, o, "hidden");
10199
10200             var st = this.dom.style;
10201             st.visibility = "visible";
10202             st.position = "absolute";
10203
10204             // clear out temp styles after slide and unwrap
10205             var after = function(){
10206                 el.fxUnwrap(wrap, r.pos, o);
10207                 st.width = r.width;
10208                 st.height = r.height;
10209                 el.afterFx(o);
10210             };
10211             // time to calc the positions
10212             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10213
10214             switch(anchor.toLowerCase()){
10215                 case "t":
10216                     wrap.setSize(b.width, 0);
10217                     st.left = st.bottom = "0";
10218                     a = {height: bh};
10219                 break;
10220                 case "l":
10221                     wrap.setSize(0, b.height);
10222                     st.right = st.top = "0";
10223                     a = {width: bw};
10224                 break;
10225                 case "r":
10226                     wrap.setSize(0, b.height);
10227                     wrap.setX(b.right);
10228                     st.left = st.top = "0";
10229                     a = {width: bw, points: pt};
10230                 break;
10231                 case "b":
10232                     wrap.setSize(b.width, 0);
10233                     wrap.setY(b.bottom);
10234                     st.left = st.top = "0";
10235                     a = {height: bh, points: pt};
10236                 break;
10237                 case "tl":
10238                     wrap.setSize(0, 0);
10239                     st.right = st.bottom = "0";
10240                     a = {width: bw, height: bh};
10241                 break;
10242                 case "bl":
10243                     wrap.setSize(0, 0);
10244                     wrap.setY(b.y+b.height);
10245                     st.right = st.top = "0";
10246                     a = {width: bw, height: bh, points: pt};
10247                 break;
10248                 case "br":
10249                     wrap.setSize(0, 0);
10250                     wrap.setXY([b.right, b.bottom]);
10251                     st.left = st.top = "0";
10252                     a = {width: bw, height: bh, points: pt};
10253                 break;
10254                 case "tr":
10255                     wrap.setSize(0, 0);
10256                     wrap.setX(b.x+b.width);
10257                     st.left = st.bottom = "0";
10258                     a = {width: bw, height: bh, points: pt};
10259                 break;
10260             }
10261             this.dom.style.visibility = "visible";
10262             wrap.show();
10263
10264             arguments.callee.anim = wrap.fxanim(a,
10265                 o,
10266                 'motion',
10267                 .5,
10268                 'easeOut', after);
10269         });
10270         return this;
10271     },
10272     
10273         /**
10274          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10275          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10276          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10277          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10278          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10279          * Usage:
10280          *<pre><code>
10281 // default: slide the element out to the top
10282 el.slideOut();
10283
10284 // custom: slide the element out to the right with a 2-second duration
10285 el.slideOut('r', { duration: 2 });
10286
10287 // common config options shown with default values
10288 el.slideOut('t', {
10289     easing: 'easeOut',
10290     duration: .5,
10291     remove: false,
10292     useDisplay: false
10293 });
10294 </code></pre>
10295          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10296          * @param {Object} options (optional) Object literal with any of the Fx config options
10297          * @return {Roo.Element} The Element
10298          */
10299     slideOut : function(anchor, o){
10300         var el = this.getFxEl();
10301         o = o || {};
10302
10303         el.queueFx(o, function(){
10304
10305             anchor = anchor || "t";
10306
10307             // restore values after effect
10308             var r = this.getFxRestore();
10309             
10310             var b = this.getBox();
10311             // fixed size for slide
10312             this.setSize(b);
10313
10314             // wrap if needed
10315             var wrap = this.fxWrap(r.pos, o, "visible");
10316
10317             var st = this.dom.style;
10318             st.visibility = "visible";
10319             st.position = "absolute";
10320
10321             wrap.setSize(b);
10322
10323             var after = function(){
10324                 if(o.useDisplay){
10325                     el.setDisplayed(false);
10326                 }else{
10327                     el.hide();
10328                 }
10329
10330                 el.fxUnwrap(wrap, r.pos, o);
10331
10332                 st.width = r.width;
10333                 st.height = r.height;
10334
10335                 el.afterFx(o);
10336             };
10337
10338             var a, zero = {to: 0};
10339             switch(anchor.toLowerCase()){
10340                 case "t":
10341                     st.left = st.bottom = "0";
10342                     a = {height: zero};
10343                 break;
10344                 case "l":
10345                     st.right = st.top = "0";
10346                     a = {width: zero};
10347                 break;
10348                 case "r":
10349                     st.left = st.top = "0";
10350                     a = {width: zero, points: {to:[b.right, b.y]}};
10351                 break;
10352                 case "b":
10353                     st.left = st.top = "0";
10354                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10355                 break;
10356                 case "tl":
10357                     st.right = st.bottom = "0";
10358                     a = {width: zero, height: zero};
10359                 break;
10360                 case "bl":
10361                     st.right = st.top = "0";
10362                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10363                 break;
10364                 case "br":
10365                     st.left = st.top = "0";
10366                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10367                 break;
10368                 case "tr":
10369                     st.left = st.bottom = "0";
10370                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10371                 break;
10372             }
10373
10374             arguments.callee.anim = wrap.fxanim(a,
10375                 o,
10376                 'motion',
10377                 .5,
10378                 "easeOut", after);
10379         });
10380         return this;
10381     },
10382
10383         /**
10384          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10385          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10386          * The element must be removed from the DOM using the 'remove' config option if desired.
10387          * Usage:
10388          *<pre><code>
10389 // default
10390 el.puff();
10391
10392 // common config options shown with default values
10393 el.puff({
10394     easing: 'easeOut',
10395     duration: .5,
10396     remove: false,
10397     useDisplay: false
10398 });
10399 </code></pre>
10400          * @param {Object} options (optional) Object literal with any of the Fx config options
10401          * @return {Roo.Element} The Element
10402          */
10403     puff : function(o){
10404         var el = this.getFxEl();
10405         o = o || {};
10406
10407         el.queueFx(o, function(){
10408             this.clearOpacity();
10409             this.show();
10410
10411             // restore values after effect
10412             var r = this.getFxRestore();
10413             var st = this.dom.style;
10414
10415             var after = function(){
10416                 if(o.useDisplay){
10417                     el.setDisplayed(false);
10418                 }else{
10419                     el.hide();
10420                 }
10421
10422                 el.clearOpacity();
10423
10424                 el.setPositioning(r.pos);
10425                 st.width = r.width;
10426                 st.height = r.height;
10427                 st.fontSize = '';
10428                 el.afterFx(o);
10429             };
10430
10431             var width = this.getWidth();
10432             var height = this.getHeight();
10433
10434             arguments.callee.anim = this.fxanim({
10435                     width : {to: this.adjustWidth(width * 2)},
10436                     height : {to: this.adjustHeight(height * 2)},
10437                     points : {by: [-(width * .5), -(height * .5)]},
10438                     opacity : {to: 0},
10439                     fontSize: {to:200, unit: "%"}
10440                 },
10441                 o,
10442                 'motion',
10443                 .5,
10444                 "easeOut", after);
10445         });
10446         return this;
10447     },
10448
10449         /**
10450          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10451          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10452          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10453          * Usage:
10454          *<pre><code>
10455 // default
10456 el.switchOff();
10457
10458 // all config options shown with default values
10459 el.switchOff({
10460     easing: 'easeIn',
10461     duration: .3,
10462     remove: false,
10463     useDisplay: false
10464 });
10465 </code></pre>
10466          * @param {Object} options (optional) Object literal with any of the Fx config options
10467          * @return {Roo.Element} The Element
10468          */
10469     switchOff : function(o){
10470         var el = this.getFxEl();
10471         o = o || {};
10472
10473         el.queueFx(o, function(){
10474             this.clearOpacity();
10475             this.clip();
10476
10477             // restore values after effect
10478             var r = this.getFxRestore();
10479             var st = this.dom.style;
10480
10481             var after = function(){
10482                 if(o.useDisplay){
10483                     el.setDisplayed(false);
10484                 }else{
10485                     el.hide();
10486                 }
10487
10488                 el.clearOpacity();
10489                 el.setPositioning(r.pos);
10490                 st.width = r.width;
10491                 st.height = r.height;
10492
10493                 el.afterFx(o);
10494             };
10495
10496             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10497                 this.clearOpacity();
10498                 (function(){
10499                     this.fxanim({
10500                         height:{to:1},
10501                         points:{by:[0, this.getHeight() * .5]}
10502                     }, o, 'motion', 0.3, 'easeIn', after);
10503                 }).defer(100, this);
10504             });
10505         });
10506         return this;
10507     },
10508
10509     /**
10510      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10511      * changed using the "attr" config option) and then fading back to the original color. If no original
10512      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10513      * Usage:
10514 <pre><code>
10515 // default: highlight background to yellow
10516 el.highlight();
10517
10518 // custom: highlight foreground text to blue for 2 seconds
10519 el.highlight("0000ff", { attr: 'color', duration: 2 });
10520
10521 // common config options shown with default values
10522 el.highlight("ffff9c", {
10523     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10524     endColor: (current color) or "ffffff",
10525     easing: 'easeIn',
10526     duration: 1
10527 });
10528 </code></pre>
10529      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10530      * @param {Object} options (optional) Object literal with any of the Fx config options
10531      * @return {Roo.Element} The Element
10532      */ 
10533     highlight : function(color, o){
10534         var el = this.getFxEl();
10535         o = o || {};
10536
10537         el.queueFx(o, function(){
10538             color = color || "ffff9c";
10539             attr = o.attr || "backgroundColor";
10540
10541             this.clearOpacity();
10542             this.show();
10543
10544             var origColor = this.getColor(attr);
10545             var restoreColor = this.dom.style[attr];
10546             endColor = (o.endColor || origColor) || "ffffff";
10547
10548             var after = function(){
10549                 el.dom.style[attr] = restoreColor;
10550                 el.afterFx(o);
10551             };
10552
10553             var a = {};
10554             a[attr] = {from: color, to: endColor};
10555             arguments.callee.anim = this.fxanim(a,
10556                 o,
10557                 'color',
10558                 1,
10559                 'easeIn', after);
10560         });
10561         return this;
10562     },
10563
10564    /**
10565     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10566     * Usage:
10567 <pre><code>
10568 // default: a single light blue ripple
10569 el.frame();
10570
10571 // custom: 3 red ripples lasting 3 seconds total
10572 el.frame("ff0000", 3, { duration: 3 });
10573
10574 // common config options shown with default values
10575 el.frame("C3DAF9", 1, {
10576     duration: 1 //duration of entire animation (not each individual ripple)
10577     // Note: Easing is not configurable and will be ignored if included
10578 });
10579 </code></pre>
10580     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10581     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10582     * @param {Object} options (optional) Object literal with any of the Fx config options
10583     * @return {Roo.Element} The Element
10584     */
10585     frame : function(color, count, o){
10586         var el = this.getFxEl();
10587         o = o || {};
10588
10589         el.queueFx(o, function(){
10590             color = color || "#C3DAF9";
10591             if(color.length == 6){
10592                 color = "#" + color;
10593             }
10594             count = count || 1;
10595             duration = o.duration || 1;
10596             this.show();
10597
10598             var b = this.getBox();
10599             var animFn = function(){
10600                 var proxy = this.createProxy({
10601
10602                      style:{
10603                         visbility:"hidden",
10604                         position:"absolute",
10605                         "z-index":"35000", // yee haw
10606                         border:"0px solid " + color
10607                      }
10608                   });
10609                 var scale = Roo.isBorderBox ? 2 : 1;
10610                 proxy.animate({
10611                     top:{from:b.y, to:b.y - 20},
10612                     left:{from:b.x, to:b.x - 20},
10613                     borderWidth:{from:0, to:10},
10614                     opacity:{from:1, to:0},
10615                     height:{from:b.height, to:(b.height + (20*scale))},
10616                     width:{from:b.width, to:(b.width + (20*scale))}
10617                 }, duration, function(){
10618                     proxy.remove();
10619                 });
10620                 if(--count > 0){
10621                      animFn.defer((duration/2)*1000, this);
10622                 }else{
10623                     el.afterFx(o);
10624                 }
10625             };
10626             animFn.call(this);
10627         });
10628         return this;
10629     },
10630
10631    /**
10632     * Creates a pause before any subsequent queued effects begin.  If there are
10633     * no effects queued after the pause it will have no effect.
10634     * Usage:
10635 <pre><code>
10636 el.pause(1);
10637 </code></pre>
10638     * @param {Number} seconds The length of time to pause (in seconds)
10639     * @return {Roo.Element} The Element
10640     */
10641     pause : function(seconds){
10642         var el = this.getFxEl();
10643         var o = {};
10644
10645         el.queueFx(o, function(){
10646             setTimeout(function(){
10647                 el.afterFx(o);
10648             }, seconds * 1000);
10649         });
10650         return this;
10651     },
10652
10653    /**
10654     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10655     * using the "endOpacity" config option.
10656     * Usage:
10657 <pre><code>
10658 // default: fade in from opacity 0 to 100%
10659 el.fadeIn();
10660
10661 // custom: fade in from opacity 0 to 75% over 2 seconds
10662 el.fadeIn({ endOpacity: .75, duration: 2});
10663
10664 // common config options shown with default values
10665 el.fadeIn({
10666     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10667     easing: 'easeOut',
10668     duration: .5
10669 });
10670 </code></pre>
10671     * @param {Object} options (optional) Object literal with any of the Fx config options
10672     * @return {Roo.Element} The Element
10673     */
10674     fadeIn : function(o){
10675         var el = this.getFxEl();
10676         o = o || {};
10677         el.queueFx(o, function(){
10678             this.setOpacity(0);
10679             this.fixDisplay();
10680             this.dom.style.visibility = 'visible';
10681             var to = o.endOpacity || 1;
10682             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10683                 o, null, .5, "easeOut", function(){
10684                 if(to == 1){
10685                     this.clearOpacity();
10686                 }
10687                 el.afterFx(o);
10688             });
10689         });
10690         return this;
10691     },
10692
10693    /**
10694     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10695     * using the "endOpacity" config option.
10696     * Usage:
10697 <pre><code>
10698 // default: fade out from the element's current opacity to 0
10699 el.fadeOut();
10700
10701 // custom: fade out from the element's current opacity to 25% over 2 seconds
10702 el.fadeOut({ endOpacity: .25, duration: 2});
10703
10704 // common config options shown with default values
10705 el.fadeOut({
10706     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10707     easing: 'easeOut',
10708     duration: .5
10709     remove: false,
10710     useDisplay: false
10711 });
10712 </code></pre>
10713     * @param {Object} options (optional) Object literal with any of the Fx config options
10714     * @return {Roo.Element} The Element
10715     */
10716     fadeOut : function(o){
10717         var el = this.getFxEl();
10718         o = o || {};
10719         el.queueFx(o, function(){
10720             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10721                 o, null, .5, "easeOut", function(){
10722                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10723                      this.dom.style.display = "none";
10724                 }else{
10725                      this.dom.style.visibility = "hidden";
10726                 }
10727                 this.clearOpacity();
10728                 el.afterFx(o);
10729             });
10730         });
10731         return this;
10732     },
10733
10734    /**
10735     * Animates the transition of an element's dimensions from a starting height/width
10736     * to an ending height/width.
10737     * Usage:
10738 <pre><code>
10739 // change height and width to 100x100 pixels
10740 el.scale(100, 100);
10741
10742 // common config options shown with default values.  The height and width will default to
10743 // the element's existing values if passed as null.
10744 el.scale(
10745     [element's width],
10746     [element's height], {
10747     easing: 'easeOut',
10748     duration: .35
10749 });
10750 </code></pre>
10751     * @param {Number} width  The new width (pass undefined to keep the original width)
10752     * @param {Number} height  The new height (pass undefined to keep the original height)
10753     * @param {Object} options (optional) Object literal with any of the Fx config options
10754     * @return {Roo.Element} The Element
10755     */
10756     scale : function(w, h, o){
10757         this.shift(Roo.apply({}, o, {
10758             width: w,
10759             height: h
10760         }));
10761         return this;
10762     },
10763
10764    /**
10765     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10766     * Any of these properties not specified in the config object will not be changed.  This effect 
10767     * requires that at least one new dimension, position or opacity setting must be passed in on
10768     * the config object in order for the function to have any effect.
10769     * Usage:
10770 <pre><code>
10771 // slide the element horizontally to x position 200 while changing the height and opacity
10772 el.shift({ x: 200, height: 50, opacity: .8 });
10773
10774 // common config options shown with default values.
10775 el.shift({
10776     width: [element's width],
10777     height: [element's height],
10778     x: [element's x position],
10779     y: [element's y position],
10780     opacity: [element's opacity],
10781     easing: 'easeOut',
10782     duration: .35
10783 });
10784 </code></pre>
10785     * @param {Object} options  Object literal with any of the Fx config options
10786     * @return {Roo.Element} The Element
10787     */
10788     shift : function(o){
10789         var el = this.getFxEl();
10790         o = o || {};
10791         el.queueFx(o, function(){
10792             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10793             if(w !== undefined){
10794                 a.width = {to: this.adjustWidth(w)};
10795             }
10796             if(h !== undefined){
10797                 a.height = {to: this.adjustHeight(h)};
10798             }
10799             if(x !== undefined || y !== undefined){
10800                 a.points = {to: [
10801                     x !== undefined ? x : this.getX(),
10802                     y !== undefined ? y : this.getY()
10803                 ]};
10804             }
10805             if(op !== undefined){
10806                 a.opacity = {to: op};
10807             }
10808             if(o.xy !== undefined){
10809                 a.points = {to: o.xy};
10810             }
10811             arguments.callee.anim = this.fxanim(a,
10812                 o, 'motion', .35, "easeOut", function(){
10813                 el.afterFx(o);
10814             });
10815         });
10816         return this;
10817     },
10818
10819         /**
10820          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10821          * ending point of the effect.
10822          * Usage:
10823          *<pre><code>
10824 // default: slide the element downward while fading out
10825 el.ghost();
10826
10827 // custom: slide the element out to the right with a 2-second duration
10828 el.ghost('r', { duration: 2 });
10829
10830 // common config options shown with default values
10831 el.ghost('b', {
10832     easing: 'easeOut',
10833     duration: .5
10834     remove: false,
10835     useDisplay: false
10836 });
10837 </code></pre>
10838          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10839          * @param {Object} options (optional) Object literal with any of the Fx config options
10840          * @return {Roo.Element} The Element
10841          */
10842     ghost : function(anchor, o){
10843         var el = this.getFxEl();
10844         o = o || {};
10845
10846         el.queueFx(o, function(){
10847             anchor = anchor || "b";
10848
10849             // restore values after effect
10850             var r = this.getFxRestore();
10851             var w = this.getWidth(),
10852                 h = this.getHeight();
10853
10854             var st = this.dom.style;
10855
10856             var after = function(){
10857                 if(o.useDisplay){
10858                     el.setDisplayed(false);
10859                 }else{
10860                     el.hide();
10861                 }
10862
10863                 el.clearOpacity();
10864                 el.setPositioning(r.pos);
10865                 st.width = r.width;
10866                 st.height = r.height;
10867
10868                 el.afterFx(o);
10869             };
10870
10871             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10872             switch(anchor.toLowerCase()){
10873                 case "t":
10874                     pt.by = [0, -h];
10875                 break;
10876                 case "l":
10877                     pt.by = [-w, 0];
10878                 break;
10879                 case "r":
10880                     pt.by = [w, 0];
10881                 break;
10882                 case "b":
10883                     pt.by = [0, h];
10884                 break;
10885                 case "tl":
10886                     pt.by = [-w, -h];
10887                 break;
10888                 case "bl":
10889                     pt.by = [-w, h];
10890                 break;
10891                 case "br":
10892                     pt.by = [w, h];
10893                 break;
10894                 case "tr":
10895                     pt.by = [w, -h];
10896                 break;
10897             }
10898
10899             arguments.callee.anim = this.fxanim(a,
10900                 o,
10901                 'motion',
10902                 .5,
10903                 "easeOut", after);
10904         });
10905         return this;
10906     },
10907
10908         /**
10909          * Ensures that all effects queued after syncFx is called on the element are
10910          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10911          * @return {Roo.Element} The Element
10912          */
10913     syncFx : function(){
10914         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10915             block : false,
10916             concurrent : true,
10917             stopFx : false
10918         });
10919         return this;
10920     },
10921
10922         /**
10923          * Ensures that all effects queued after sequenceFx is called on the element are
10924          * run in sequence.  This is the opposite of {@link #syncFx}.
10925          * @return {Roo.Element} The Element
10926          */
10927     sequenceFx : function(){
10928         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10929             block : false,
10930             concurrent : false,
10931             stopFx : false
10932         });
10933         return this;
10934     },
10935
10936         /* @private */
10937     nextFx : function(){
10938         var ef = this.fxQueue[0];
10939         if(ef){
10940             ef.call(this);
10941         }
10942     },
10943
10944         /**
10945          * Returns true if the element has any effects actively running or queued, else returns false.
10946          * @return {Boolean} True if element has active effects, else false
10947          */
10948     hasActiveFx : function(){
10949         return this.fxQueue && this.fxQueue[0];
10950     },
10951
10952         /**
10953          * Stops any running effects and clears the element's internal effects queue if it contains
10954          * any additional effects that haven't started yet.
10955          * @return {Roo.Element} The Element
10956          */
10957     stopFx : function(){
10958         if(this.hasActiveFx()){
10959             var cur = this.fxQueue[0];
10960             if(cur && cur.anim && cur.anim.isAnimated()){
10961                 this.fxQueue = [cur]; // clear out others
10962                 cur.anim.stop(true);
10963             }
10964         }
10965         return this;
10966     },
10967
10968         /* @private */
10969     beforeFx : function(o){
10970         if(this.hasActiveFx() && !o.concurrent){
10971            if(o.stopFx){
10972                this.stopFx();
10973                return true;
10974            }
10975            return false;
10976         }
10977         return true;
10978     },
10979
10980         /**
10981          * Returns true if the element is currently blocking so that no other effect can be queued
10982          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10983          * used to ensure that an effect initiated by a user action runs to completion prior to the
10984          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10985          * @return {Boolean} True if blocking, else false
10986          */
10987     hasFxBlock : function(){
10988         var q = this.fxQueue;
10989         return q && q[0] && q[0].block;
10990     },
10991
10992         /* @private */
10993     queueFx : function(o, fn){
10994         if(!this.fxQueue){
10995             this.fxQueue = [];
10996         }
10997         if(!this.hasFxBlock()){
10998             Roo.applyIf(o, this.fxDefaults);
10999             if(!o.concurrent){
11000                 var run = this.beforeFx(o);
11001                 fn.block = o.block;
11002                 this.fxQueue.push(fn);
11003                 if(run){
11004                     this.nextFx();
11005                 }
11006             }else{
11007                 fn.call(this);
11008             }
11009         }
11010         return this;
11011     },
11012
11013         /* @private */
11014     fxWrap : function(pos, o, vis){
11015         var wrap;
11016         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11017             var wrapXY;
11018             if(o.fixPosition){
11019                 wrapXY = this.getXY();
11020             }
11021             var div = document.createElement("div");
11022             div.style.visibility = vis;
11023             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11024             wrap.setPositioning(pos);
11025             if(wrap.getStyle("position") == "static"){
11026                 wrap.position("relative");
11027             }
11028             this.clearPositioning('auto');
11029             wrap.clip();
11030             wrap.dom.appendChild(this.dom);
11031             if(wrapXY){
11032                 wrap.setXY(wrapXY);
11033             }
11034         }
11035         return wrap;
11036     },
11037
11038         /* @private */
11039     fxUnwrap : function(wrap, pos, o){
11040         this.clearPositioning();
11041         this.setPositioning(pos);
11042         if(!o.wrap){
11043             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11044             wrap.remove();
11045         }
11046     },
11047
11048         /* @private */
11049     getFxRestore : function(){
11050         var st = this.dom.style;
11051         return {pos: this.getPositioning(), width: st.width, height : st.height};
11052     },
11053
11054         /* @private */
11055     afterFx : function(o){
11056         if(o.afterStyle){
11057             this.applyStyles(o.afterStyle);
11058         }
11059         if(o.afterCls){
11060             this.addClass(o.afterCls);
11061         }
11062         if(o.remove === true){
11063             this.remove();
11064         }
11065         Roo.callback(o.callback, o.scope, [this]);
11066         if(!o.concurrent){
11067             this.fxQueue.shift();
11068             this.nextFx();
11069         }
11070     },
11071
11072         /* @private */
11073     getFxEl : function(){ // support for composite element fx
11074         return Roo.get(this.dom);
11075     },
11076
11077         /* @private */
11078     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11079         animType = animType || 'run';
11080         opt = opt || {};
11081         var anim = Roo.lib.Anim[animType](
11082             this.dom, args,
11083             (opt.duration || defaultDur) || .35,
11084             (opt.easing || defaultEase) || 'easeOut',
11085             function(){
11086                 Roo.callback(cb, this);
11087             },
11088             this
11089         );
11090         opt.anim = anim;
11091         return anim;
11092     }
11093 };
11094
11095 // backwords compat
11096 Roo.Fx.resize = Roo.Fx.scale;
11097
11098 //When included, Roo.Fx is automatically applied to Element so that all basic
11099 //effects are available directly via the Element API
11100 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11101  * Based on:
11102  * Ext JS Library 1.1.1
11103  * Copyright(c) 2006-2007, Ext JS, LLC.
11104  *
11105  * Originally Released Under LGPL - original licence link has changed is not relivant.
11106  *
11107  * Fork - LGPL
11108  * <script type="text/javascript">
11109  */
11110
11111
11112 /**
11113  * @class Roo.CompositeElement
11114  * Standard composite class. Creates a Roo.Element for every element in the collection.
11115  * <br><br>
11116  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11117  * actions will be performed on all the elements in this collection.</b>
11118  * <br><br>
11119  * All methods return <i>this</i> and can be chained.
11120  <pre><code>
11121  var els = Roo.select("#some-el div.some-class", true);
11122  // or select directly from an existing element
11123  var el = Roo.get('some-el');
11124  el.select('div.some-class', true);
11125
11126  els.setWidth(100); // all elements become 100 width
11127  els.hide(true); // all elements fade out and hide
11128  // or
11129  els.setWidth(100).hide(true);
11130  </code></pre>
11131  */
11132 Roo.CompositeElement = function(els){
11133     this.elements = [];
11134     this.addElements(els);
11135 };
11136 Roo.CompositeElement.prototype = {
11137     isComposite: true,
11138     addElements : function(els){
11139         if(!els) {
11140             return this;
11141         }
11142         if(typeof els == "string"){
11143             els = Roo.Element.selectorFunction(els);
11144         }
11145         var yels = this.elements;
11146         var index = yels.length-1;
11147         for(var i = 0, len = els.length; i < len; i++) {
11148                 yels[++index] = Roo.get(els[i]);
11149         }
11150         return this;
11151     },
11152
11153     /**
11154     * Clears this composite and adds the elements returned by the passed selector.
11155     * @param {String/Array} els A string CSS selector, an array of elements or an element
11156     * @return {CompositeElement} this
11157     */
11158     fill : function(els){
11159         this.elements = [];
11160         this.add(els);
11161         return this;
11162     },
11163
11164     /**
11165     * Filters this composite to only elements that match the passed selector.
11166     * @param {String} selector A string CSS selector
11167     * @param {Boolean} inverse return inverse filter (not matches)
11168     * @return {CompositeElement} this
11169     */
11170     filter : function(selector, inverse){
11171         var els = [];
11172         inverse = inverse || false;
11173         this.each(function(el){
11174             var match = inverse ? !el.is(selector) : el.is(selector);
11175             if(match){
11176                 els[els.length] = el.dom;
11177             }
11178         });
11179         this.fill(els);
11180         return this;
11181     },
11182
11183     invoke : function(fn, args){
11184         var els = this.elements;
11185         for(var i = 0, len = els.length; i < len; i++) {
11186                 Roo.Element.prototype[fn].apply(els[i], args);
11187         }
11188         return this;
11189     },
11190     /**
11191     * Adds elements to this composite.
11192     * @param {String/Array} els A string CSS selector, an array of elements or an element
11193     * @return {CompositeElement} this
11194     */
11195     add : function(els){
11196         if(typeof els == "string"){
11197             this.addElements(Roo.Element.selectorFunction(els));
11198         }else if(els.length !== undefined){
11199             this.addElements(els);
11200         }else{
11201             this.addElements([els]);
11202         }
11203         return this;
11204     },
11205     /**
11206     * Calls the passed function passing (el, this, index) for each element in this composite.
11207     * @param {Function} fn The function to call
11208     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11209     * @return {CompositeElement} this
11210     */
11211     each : function(fn, scope){
11212         var els = this.elements;
11213         for(var i = 0, len = els.length; i < len; i++){
11214             if(fn.call(scope || els[i], els[i], this, i) === false) {
11215                 break;
11216             }
11217         }
11218         return this;
11219     },
11220
11221     /**
11222      * Returns the Element object at the specified index
11223      * @param {Number} index
11224      * @return {Roo.Element}
11225      */
11226     item : function(index){
11227         return this.elements[index] || null;
11228     },
11229
11230     /**
11231      * Returns the first Element
11232      * @return {Roo.Element}
11233      */
11234     first : function(){
11235         return this.item(0);
11236     },
11237
11238     /**
11239      * Returns the last Element
11240      * @return {Roo.Element}
11241      */
11242     last : function(){
11243         return this.item(this.elements.length-1);
11244     },
11245
11246     /**
11247      * Returns the number of elements in this composite
11248      * @return Number
11249      */
11250     getCount : function(){
11251         return this.elements.length;
11252     },
11253
11254     /**
11255      * Returns true if this composite contains the passed element
11256      * @return Boolean
11257      */
11258     contains : function(el){
11259         return this.indexOf(el) !== -1;
11260     },
11261
11262     /**
11263      * Returns true if this composite contains the passed element
11264      * @return Boolean
11265      */
11266     indexOf : function(el){
11267         return this.elements.indexOf(Roo.get(el));
11268     },
11269
11270
11271     /**
11272     * Removes the specified element(s).
11273     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11274     * or an array of any of those.
11275     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11276     * @return {CompositeElement} this
11277     */
11278     removeElement : function(el, removeDom){
11279         if(el instanceof Array){
11280             for(var i = 0, len = el.length; i < len; i++){
11281                 this.removeElement(el[i]);
11282             }
11283             return this;
11284         }
11285         var index = typeof el == 'number' ? el : this.indexOf(el);
11286         if(index !== -1){
11287             if(removeDom){
11288                 var d = this.elements[index];
11289                 if(d.dom){
11290                     d.remove();
11291                 }else{
11292                     d.parentNode.removeChild(d);
11293                 }
11294             }
11295             this.elements.splice(index, 1);
11296         }
11297         return this;
11298     },
11299
11300     /**
11301     * Replaces the specified element with the passed element.
11302     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11303     * to replace.
11304     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11305     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11306     * @return {CompositeElement} this
11307     */
11308     replaceElement : function(el, replacement, domReplace){
11309         var index = typeof el == 'number' ? el : this.indexOf(el);
11310         if(index !== -1){
11311             if(domReplace){
11312                 this.elements[index].replaceWith(replacement);
11313             }else{
11314                 this.elements.splice(index, 1, Roo.get(replacement))
11315             }
11316         }
11317         return this;
11318     },
11319
11320     /**
11321      * Removes all elements.
11322      */
11323     clear : function(){
11324         this.elements = [];
11325     }
11326 };
11327 (function(){
11328     Roo.CompositeElement.createCall = function(proto, fnName){
11329         if(!proto[fnName]){
11330             proto[fnName] = function(){
11331                 return this.invoke(fnName, arguments);
11332             };
11333         }
11334     };
11335     for(var fnName in Roo.Element.prototype){
11336         if(typeof Roo.Element.prototype[fnName] == "function"){
11337             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11338         }
11339     };
11340 })();
11341 /*
11342  * Based on:
11343  * Ext JS Library 1.1.1
11344  * Copyright(c) 2006-2007, Ext JS, LLC.
11345  *
11346  * Originally Released Under LGPL - original licence link has changed is not relivant.
11347  *
11348  * Fork - LGPL
11349  * <script type="text/javascript">
11350  */
11351
11352 /**
11353  * @class Roo.CompositeElementLite
11354  * @extends Roo.CompositeElement
11355  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11356  <pre><code>
11357  var els = Roo.select("#some-el div.some-class");
11358  // or select directly from an existing element
11359  var el = Roo.get('some-el');
11360  el.select('div.some-class');
11361
11362  els.setWidth(100); // all elements become 100 width
11363  els.hide(true); // all elements fade out and hide
11364  // or
11365  els.setWidth(100).hide(true);
11366  </code></pre><br><br>
11367  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11368  * actions will be performed on all the elements in this collection.</b>
11369  */
11370 Roo.CompositeElementLite = function(els){
11371     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11372     this.el = new Roo.Element.Flyweight();
11373 };
11374 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11375     addElements : function(els){
11376         if(els){
11377             if(els instanceof Array){
11378                 this.elements = this.elements.concat(els);
11379             }else{
11380                 var yels = this.elements;
11381                 var index = yels.length-1;
11382                 for(var i = 0, len = els.length; i < len; i++) {
11383                     yels[++index] = els[i];
11384                 }
11385             }
11386         }
11387         return this;
11388     },
11389     invoke : function(fn, args){
11390         var els = this.elements;
11391         var el = this.el;
11392         for(var i = 0, len = els.length; i < len; i++) {
11393             el.dom = els[i];
11394                 Roo.Element.prototype[fn].apply(el, args);
11395         }
11396         return this;
11397     },
11398     /**
11399      * Returns a flyweight Element of the dom element object at the specified index
11400      * @param {Number} index
11401      * @return {Roo.Element}
11402      */
11403     item : function(index){
11404         if(!this.elements[index]){
11405             return null;
11406         }
11407         this.el.dom = this.elements[index];
11408         return this.el;
11409     },
11410
11411     // fixes scope with flyweight
11412     addListener : function(eventName, handler, scope, opt){
11413         var els = this.elements;
11414         for(var i = 0, len = els.length; i < len; i++) {
11415             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11416         }
11417         return this;
11418     },
11419
11420     /**
11421     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11422     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11423     * a reference to the dom node, use el.dom.</b>
11424     * @param {Function} fn The function to call
11425     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11426     * @return {CompositeElement} this
11427     */
11428     each : function(fn, scope){
11429         var els = this.elements;
11430         var el = this.el;
11431         for(var i = 0, len = els.length; i < len; i++){
11432             el.dom = els[i];
11433                 if(fn.call(scope || el, el, this, i) === false){
11434                 break;
11435             }
11436         }
11437         return this;
11438     },
11439
11440     indexOf : function(el){
11441         return this.elements.indexOf(Roo.getDom(el));
11442     },
11443
11444     replaceElement : function(el, replacement, domReplace){
11445         var index = typeof el == 'number' ? el : this.indexOf(el);
11446         if(index !== -1){
11447             replacement = Roo.getDom(replacement);
11448             if(domReplace){
11449                 var d = this.elements[index];
11450                 d.parentNode.insertBefore(replacement, d);
11451                 d.parentNode.removeChild(d);
11452             }
11453             this.elements.splice(index, 1, replacement);
11454         }
11455         return this;
11456     }
11457 });
11458 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11459
11460 /*
11461  * Based on:
11462  * Ext JS Library 1.1.1
11463  * Copyright(c) 2006-2007, Ext JS, LLC.
11464  *
11465  * Originally Released Under LGPL - original licence link has changed is not relivant.
11466  *
11467  * Fork - LGPL
11468  * <script type="text/javascript">
11469  */
11470
11471  
11472
11473 /**
11474  * @class Roo.data.Connection
11475  * @extends Roo.util.Observable
11476  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11477  * either to a configured URL, or to a URL specified at request time. 
11478  * 
11479  * Requests made by this class are asynchronous, and will return immediately. No data from
11480  * the server will be available to the statement immediately following the {@link #request} call.
11481  * To process returned data, use a callback in the request options object, or an event listener.
11482  * 
11483  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11484  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11485  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11486  * property and, if present, the IFRAME's XML document as the responseXML property.
11487  * 
11488  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11489  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11490  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11491  * standard DOM methods.
11492  * @constructor
11493  * @param {Object} config a configuration object.
11494  */
11495 Roo.data.Connection = function(config){
11496     Roo.apply(this, config);
11497     this.addEvents({
11498         /**
11499          * @event beforerequest
11500          * Fires before a network request is made to retrieve a data object.
11501          * @param {Connection} conn This Connection object.
11502          * @param {Object} options The options config object passed to the {@link #request} method.
11503          */
11504         "beforerequest" : true,
11505         /**
11506          * @event requestcomplete
11507          * Fires if the request was successfully completed.
11508          * @param {Connection} conn This Connection object.
11509          * @param {Object} response The XHR object containing the response data.
11510          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11511          * @param {Object} options The options config object passed to the {@link #request} method.
11512          */
11513         "requestcomplete" : true,
11514         /**
11515          * @event requestexception
11516          * Fires if an error HTTP status was returned from the server.
11517          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11518          * @param {Connection} conn This Connection object.
11519          * @param {Object} response The XHR object containing the response data.
11520          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11521          * @param {Object} options The options config object passed to the {@link #request} method.
11522          */
11523         "requestexception" : true
11524     });
11525     Roo.data.Connection.superclass.constructor.call(this);
11526 };
11527
11528 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11529     /**
11530      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11531      */
11532     /**
11533      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11534      * extra parameters to each request made by this object. (defaults to undefined)
11535      */
11536     /**
11537      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11538      *  to each request made by this object. (defaults to undefined)
11539      */
11540     /**
11541      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11542      */
11543     /**
11544      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11545      */
11546     timeout : 30000,
11547     /**
11548      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11549      * @type Boolean
11550      */
11551     autoAbort:false,
11552
11553     /**
11554      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11555      * @type Boolean
11556      */
11557     disableCaching: true,
11558
11559     /**
11560      * Sends an HTTP request to a remote server.
11561      * @param {Object} options An object which may contain the following properties:<ul>
11562      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11563      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11564      * request, a url encoded string or a function to call to get either.</li>
11565      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11566      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11567      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11568      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11569      * <li>options {Object} The parameter to the request call.</li>
11570      * <li>success {Boolean} True if the request succeeded.</li>
11571      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11572      * </ul></li>
11573      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11574      * The callback is passed the following parameters:<ul>
11575      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11576      * <li>options {Object} The parameter to the request call.</li>
11577      * </ul></li>
11578      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11579      * The callback is passed the following parameters:<ul>
11580      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11581      * <li>options {Object} The parameter to the request call.</li>
11582      * </ul></li>
11583      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11584      * for the callback function. Defaults to the browser window.</li>
11585      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11586      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11587      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11588      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11589      * params for the post data. Any params will be appended to the URL.</li>
11590      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11591      * </ul>
11592      * @return {Number} transactionId
11593      */
11594     request : function(o){
11595         if(this.fireEvent("beforerequest", this, o) !== false){
11596             var p = o.params;
11597
11598             if(typeof p == "function"){
11599                 p = p.call(o.scope||window, o);
11600             }
11601             if(typeof p == "object"){
11602                 p = Roo.urlEncode(o.params);
11603             }
11604             if(this.extraParams){
11605                 var extras = Roo.urlEncode(this.extraParams);
11606                 p = p ? (p + '&' + extras) : extras;
11607             }
11608
11609             var url = o.url || this.url;
11610             if(typeof url == 'function'){
11611                 url = url.call(o.scope||window, o);
11612             }
11613
11614             if(o.form){
11615                 var form = Roo.getDom(o.form);
11616                 url = url || form.action;
11617
11618                 var enctype = form.getAttribute("enctype");
11619                 
11620                 if (o.formData) {
11621                     return this.doFormDataUpload(o,p,url);
11622                 }
11623                 
11624                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11625                     return this.doFormUpload(o, p, url);
11626                 }
11627                 var f = Roo.lib.Ajax.serializeForm(form);
11628                 p = p ? (p + '&' + f) : f;
11629             }
11630
11631             var hs = o.headers;
11632             if(this.defaultHeaders){
11633                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11634                 if(!o.headers){
11635                     o.headers = hs;
11636                 }
11637             }
11638
11639             var cb = {
11640                 success: this.handleResponse,
11641                 failure: this.handleFailure,
11642                 scope: this,
11643                 argument: {options: o},
11644                 timeout : o.timeout || this.timeout
11645             };
11646
11647             var method = o.method||this.method||(p ? "POST" : "GET");
11648
11649             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11650                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11651             }
11652
11653             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11654                 if(o.autoAbort){
11655                     this.abort();
11656                 }
11657             }else if(this.autoAbort !== false){
11658                 this.abort();
11659             }
11660
11661             if((method == 'GET' && p) || o.xmlData){
11662                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11663                 p = '';
11664             }
11665             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11666             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11667             Roo.lib.Ajax.useDefaultHeader == true;
11668             return this.transId;
11669         }else{
11670             Roo.callback(o.callback, o.scope, [o, null, null]);
11671             return null;
11672         }
11673     },
11674
11675     /**
11676      * Determine whether this object has a request outstanding.
11677      * @param {Number} transactionId (Optional) defaults to the last transaction
11678      * @return {Boolean} True if there is an outstanding request.
11679      */
11680     isLoading : function(transId){
11681         if(transId){
11682             return Roo.lib.Ajax.isCallInProgress(transId);
11683         }else{
11684             return this.transId ? true : false;
11685         }
11686     },
11687
11688     /**
11689      * Aborts any outstanding request.
11690      * @param {Number} transactionId (Optional) defaults to the last transaction
11691      */
11692     abort : function(transId){
11693         if(transId || this.isLoading()){
11694             Roo.lib.Ajax.abort(transId || this.transId);
11695         }
11696     },
11697
11698     // private
11699     handleResponse : function(response){
11700         this.transId = false;
11701         var options = response.argument.options;
11702         response.argument = options ? options.argument : null;
11703         this.fireEvent("requestcomplete", this, response, options);
11704         Roo.callback(options.success, options.scope, [response, options]);
11705         Roo.callback(options.callback, options.scope, [options, true, response]);
11706     },
11707
11708     // private
11709     handleFailure : function(response, e){
11710         this.transId = false;
11711         var options = response.argument.options;
11712         response.argument = options ? options.argument : null;
11713         this.fireEvent("requestexception", this, response, options, e);
11714         Roo.callback(options.failure, options.scope, [response, options]);
11715         Roo.callback(options.callback, options.scope, [options, false, response]);
11716     },
11717
11718     // private
11719     doFormUpload : function(o, ps, url){
11720         var id = Roo.id();
11721         var frame = document.createElement('iframe');
11722         frame.id = id;
11723         frame.name = id;
11724         frame.className = 'x-hidden';
11725         if(Roo.isIE){
11726             frame.src = Roo.SSL_SECURE_URL;
11727         }
11728         document.body.appendChild(frame);
11729
11730         if(Roo.isIE){
11731            document.frames[id].name = id;
11732         }
11733
11734         var form = Roo.getDom(o.form);
11735         form.target = id;
11736         form.method = 'POST';
11737         form.enctype = form.encoding = 'multipart/form-data';
11738         if(url){
11739             form.action = url;
11740         }
11741
11742         var hiddens, hd;
11743         if(ps){ // add dynamic params
11744             hiddens = [];
11745             ps = Roo.urlDecode(ps, false);
11746             for(var k in ps){
11747                 if(ps.hasOwnProperty(k)){
11748                     hd = document.createElement('input');
11749                     hd.type = 'hidden';
11750                     hd.name = k;
11751                     hd.value = ps[k];
11752                     form.appendChild(hd);
11753                     hiddens.push(hd);
11754                 }
11755             }
11756         }
11757
11758         function cb(){
11759             var r = {  // bogus response object
11760                 responseText : '',
11761                 responseXML : null
11762             };
11763
11764             r.argument = o ? o.argument : null;
11765
11766             try { //
11767                 var doc;
11768                 if(Roo.isIE){
11769                     doc = frame.contentWindow.document;
11770                 }else {
11771                     doc = (frame.contentDocument || window.frames[id].document);
11772                 }
11773                 if(doc && doc.body){
11774                     r.responseText = doc.body.innerHTML;
11775                 }
11776                 if(doc && doc.XMLDocument){
11777                     r.responseXML = doc.XMLDocument;
11778                 }else {
11779                     r.responseXML = doc;
11780                 }
11781             }
11782             catch(e) {
11783                 // ignore
11784             }
11785
11786             Roo.EventManager.removeListener(frame, 'load', cb, this);
11787
11788             this.fireEvent("requestcomplete", this, r, o);
11789             Roo.callback(o.success, o.scope, [r, o]);
11790             Roo.callback(o.callback, o.scope, [o, true, r]);
11791
11792             setTimeout(function(){document.body.removeChild(frame);}, 100);
11793         }
11794
11795         Roo.EventManager.on(frame, 'load', cb, this);
11796         form.submit();
11797
11798         if(hiddens){ // remove dynamic params
11799             for(var i = 0, len = hiddens.length; i < len; i++){
11800                 form.removeChild(hiddens[i]);
11801             }
11802         }
11803     },
11804     // this is a 'formdata version???'
11805     
11806     
11807     doFormDataUpload : function(o, ps, url)
11808     {
11809         var form = Roo.getDom(o.form);
11810         form.enctype = form.encoding = 'multipart/form-data';
11811         var formData = o.formData === true ? new FormData(form) : o.formData;
11812       
11813         var cb = {
11814             success: this.handleResponse,
11815             failure: this.handleFailure,
11816             scope: this,
11817             argument: {options: o},
11818             timeout : o.timeout || this.timeout
11819         };
11820  
11821         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11822             if(o.autoAbort){
11823                 this.abort();
11824             }
11825         }else if(this.autoAbort !== false){
11826             this.abort();
11827         }
11828
11829         //Roo.lib.Ajax.defaultPostHeader = null;
11830         Roo.lib.Ajax.useDefaultHeader = false;
11831         this.transId = Roo.lib.Ajax.request( "POST", url, cb, o.formData, o);
11832         Roo.lib.Ajax.useDefaultHeader = true;
11833  
11834          
11835     }
11836     
11837 });
11838 /*
11839  * Based on:
11840  * Ext JS Library 1.1.1
11841  * Copyright(c) 2006-2007, Ext JS, LLC.
11842  *
11843  * Originally Released Under LGPL - original licence link has changed is not relivant.
11844  *
11845  * Fork - LGPL
11846  * <script type="text/javascript">
11847  */
11848  
11849 /**
11850  * Global Ajax request class.
11851  * 
11852  * @class Roo.Ajax
11853  * @extends Roo.data.Connection
11854  * @static
11855  * 
11856  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11857  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11858  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11859  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11860  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11861  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11862  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11863  */
11864 Roo.Ajax = new Roo.data.Connection({
11865     // fix up the docs
11866     /**
11867      * @scope Roo.Ajax
11868      * @type {Boolear} 
11869      */
11870     autoAbort : false,
11871
11872     /**
11873      * Serialize the passed form into a url encoded string
11874      * @scope Roo.Ajax
11875      * @param {String/HTMLElement} form
11876      * @return {String}
11877      */
11878     serializeForm : function(form){
11879         return Roo.lib.Ajax.serializeForm(form);
11880     }
11881 });/*
11882  * Based on:
11883  * Ext JS Library 1.1.1
11884  * Copyright(c) 2006-2007, Ext JS, LLC.
11885  *
11886  * Originally Released Under LGPL - original licence link has changed is not relivant.
11887  *
11888  * Fork - LGPL
11889  * <script type="text/javascript">
11890  */
11891
11892  
11893 /**
11894  * @class Roo.UpdateManager
11895  * @extends Roo.util.Observable
11896  * Provides AJAX-style update for Element object.<br><br>
11897  * Usage:<br>
11898  * <pre><code>
11899  * // Get it from a Roo.Element object
11900  * var el = Roo.get("foo");
11901  * var mgr = el.getUpdateManager();
11902  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11903  * ...
11904  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11905  * <br>
11906  * // or directly (returns the same UpdateManager instance)
11907  * var mgr = new Roo.UpdateManager("myElementId");
11908  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11909  * mgr.on("update", myFcnNeedsToKnow);
11910  * <br>
11911    // short handed call directly from the element object
11912    Roo.get("foo").load({
11913         url: "bar.php",
11914         scripts:true,
11915         params: "for=bar",
11916         text: "Loading Foo..."
11917    });
11918  * </code></pre>
11919  * @constructor
11920  * Create new UpdateManager directly.
11921  * @param {String/HTMLElement/Roo.Element} el The element to update
11922  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11923  */
11924 Roo.UpdateManager = function(el, forceNew){
11925     el = Roo.get(el);
11926     if(!forceNew && el.updateManager){
11927         return el.updateManager;
11928     }
11929     /**
11930      * The Element object
11931      * @type Roo.Element
11932      */
11933     this.el = el;
11934     /**
11935      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11936      * @type String
11937      */
11938     this.defaultUrl = null;
11939
11940     this.addEvents({
11941         /**
11942          * @event beforeupdate
11943          * Fired before an update is made, return false from your handler and the update is cancelled.
11944          * @param {Roo.Element} el
11945          * @param {String/Object/Function} url
11946          * @param {String/Object} params
11947          */
11948         "beforeupdate": true,
11949         /**
11950          * @event update
11951          * Fired after successful update is made.
11952          * @param {Roo.Element} el
11953          * @param {Object} oResponseObject The response Object
11954          */
11955         "update": true,
11956         /**
11957          * @event failure
11958          * Fired on update failure.
11959          * @param {Roo.Element} el
11960          * @param {Object} oResponseObject The response Object
11961          */
11962         "failure": true
11963     });
11964     var d = Roo.UpdateManager.defaults;
11965     /**
11966      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11967      * @type String
11968      */
11969     this.sslBlankUrl = d.sslBlankUrl;
11970     /**
11971      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11972      * @type Boolean
11973      */
11974     this.disableCaching = d.disableCaching;
11975     /**
11976      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11977      * @type String
11978      */
11979     this.indicatorText = d.indicatorText;
11980     /**
11981      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11982      * @type String
11983      */
11984     this.showLoadIndicator = d.showLoadIndicator;
11985     /**
11986      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11987      * @type Number
11988      */
11989     this.timeout = d.timeout;
11990
11991     /**
11992      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11993      * @type Boolean
11994      */
11995     this.loadScripts = d.loadScripts;
11996
11997     /**
11998      * Transaction object of current executing transaction
11999      */
12000     this.transaction = null;
12001
12002     /**
12003      * @private
12004      */
12005     this.autoRefreshProcId = null;
12006     /**
12007      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12008      * @type Function
12009      */
12010     this.refreshDelegate = this.refresh.createDelegate(this);
12011     /**
12012      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12013      * @type Function
12014      */
12015     this.updateDelegate = this.update.createDelegate(this);
12016     /**
12017      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12018      * @type Function
12019      */
12020     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12021     /**
12022      * @private
12023      */
12024     this.successDelegate = this.processSuccess.createDelegate(this);
12025     /**
12026      * @private
12027      */
12028     this.failureDelegate = this.processFailure.createDelegate(this);
12029
12030     if(!this.renderer){
12031      /**
12032       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12033       */
12034     this.renderer = new Roo.UpdateManager.BasicRenderer();
12035     }
12036     
12037     Roo.UpdateManager.superclass.constructor.call(this);
12038 };
12039
12040 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12041     /**
12042      * Get the Element this UpdateManager is bound to
12043      * @return {Roo.Element} The element
12044      */
12045     getEl : function(){
12046         return this.el;
12047     },
12048     /**
12049      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12050      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
12051 <pre><code>
12052 um.update({<br/>
12053     url: "your-url.php",<br/>
12054     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12055     callback: yourFunction,<br/>
12056     scope: yourObject, //(optional scope)  <br/>
12057     discardUrl: false, <br/>
12058     nocache: false,<br/>
12059     text: "Loading...",<br/>
12060     timeout: 30,<br/>
12061     scripts: false<br/>
12062 });
12063 </code></pre>
12064      * The only required property is url. The optional properties nocache, text and scripts
12065      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12066      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
12067      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12068      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
12069      */
12070     update : function(url, params, callback, discardUrl){
12071         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12072             var method = this.method,
12073                 cfg;
12074             if(typeof url == "object"){ // must be config object
12075                 cfg = url;
12076                 url = cfg.url;
12077                 params = params || cfg.params;
12078                 callback = callback || cfg.callback;
12079                 discardUrl = discardUrl || cfg.discardUrl;
12080                 if(callback && cfg.scope){
12081                     callback = callback.createDelegate(cfg.scope);
12082                 }
12083                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12084                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12085                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12086                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12087                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12088             }
12089             this.showLoading();
12090             if(!discardUrl){
12091                 this.defaultUrl = url;
12092             }
12093             if(typeof url == "function"){
12094                 url = url.call(this);
12095             }
12096
12097             method = method || (params ? "POST" : "GET");
12098             if(method == "GET"){
12099                 url = this.prepareUrl(url);
12100             }
12101
12102             var o = Roo.apply(cfg ||{}, {
12103                 url : url,
12104                 params: params,
12105                 success: this.successDelegate,
12106                 failure: this.failureDelegate,
12107                 callback: undefined,
12108                 timeout: (this.timeout*1000),
12109                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12110             });
12111             Roo.log("updated manager called with timeout of " + o.timeout);
12112             this.transaction = Roo.Ajax.request(o);
12113         }
12114     },
12115
12116     /**
12117      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
12118      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12119      * @param {String/HTMLElement} form The form Id or form element
12120      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12121      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12122      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12123      */
12124     formUpdate : function(form, url, reset, callback){
12125         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12126             if(typeof url == "function"){
12127                 url = url.call(this);
12128             }
12129             form = Roo.getDom(form);
12130             this.transaction = Roo.Ajax.request({
12131                 form: form,
12132                 url:url,
12133                 success: this.successDelegate,
12134                 failure: this.failureDelegate,
12135                 timeout: (this.timeout*1000),
12136                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12137             });
12138             this.showLoading.defer(1, this);
12139         }
12140     },
12141
12142     /**
12143      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12144      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12145      */
12146     refresh : function(callback){
12147         if(this.defaultUrl == null){
12148             return;
12149         }
12150         this.update(this.defaultUrl, null, callback, true);
12151     },
12152
12153     /**
12154      * Set this element to auto refresh.
12155      * @param {Number} interval How often to update (in seconds).
12156      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
12157      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
12158      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12159      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12160      */
12161     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12162         if(refreshNow){
12163             this.update(url || this.defaultUrl, params, callback, true);
12164         }
12165         if(this.autoRefreshProcId){
12166             clearInterval(this.autoRefreshProcId);
12167         }
12168         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12169     },
12170
12171     /**
12172      * Stop auto refresh on this element.
12173      */
12174      stopAutoRefresh : function(){
12175         if(this.autoRefreshProcId){
12176             clearInterval(this.autoRefreshProcId);
12177             delete this.autoRefreshProcId;
12178         }
12179     },
12180
12181     isAutoRefreshing : function(){
12182        return this.autoRefreshProcId ? true : false;
12183     },
12184     /**
12185      * Called to update the element to "Loading" state. Override to perform custom action.
12186      */
12187     showLoading : function(){
12188         if(this.showLoadIndicator){
12189             this.el.update(this.indicatorText);
12190         }
12191     },
12192
12193     /**
12194      * Adds unique parameter to query string if disableCaching = true
12195      * @private
12196      */
12197     prepareUrl : function(url){
12198         if(this.disableCaching){
12199             var append = "_dc=" + (new Date().getTime());
12200             if(url.indexOf("?") !== -1){
12201                 url += "&" + append;
12202             }else{
12203                 url += "?" + append;
12204             }
12205         }
12206         return url;
12207     },
12208
12209     /**
12210      * @private
12211      */
12212     processSuccess : function(response){
12213         this.transaction = null;
12214         if(response.argument.form && response.argument.reset){
12215             try{ // put in try/catch since some older FF releases had problems with this
12216                 response.argument.form.reset();
12217             }catch(e){}
12218         }
12219         if(this.loadScripts){
12220             this.renderer.render(this.el, response, this,
12221                 this.updateComplete.createDelegate(this, [response]));
12222         }else{
12223             this.renderer.render(this.el, response, this);
12224             this.updateComplete(response);
12225         }
12226     },
12227
12228     updateComplete : function(response){
12229         this.fireEvent("update", this.el, response);
12230         if(typeof response.argument.callback == "function"){
12231             response.argument.callback(this.el, true, response);
12232         }
12233     },
12234
12235     /**
12236      * @private
12237      */
12238     processFailure : function(response){
12239         this.transaction = null;
12240         this.fireEvent("failure", this.el, response);
12241         if(typeof response.argument.callback == "function"){
12242             response.argument.callback(this.el, false, response);
12243         }
12244     },
12245
12246     /**
12247      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12248      * @param {Object} renderer The object implementing the render() method
12249      */
12250     setRenderer : function(renderer){
12251         this.renderer = renderer;
12252     },
12253
12254     getRenderer : function(){
12255        return this.renderer;
12256     },
12257
12258     /**
12259      * Set the defaultUrl used for updates
12260      * @param {String/Function} defaultUrl The url or a function to call to get the url
12261      */
12262     setDefaultUrl : function(defaultUrl){
12263         this.defaultUrl = defaultUrl;
12264     },
12265
12266     /**
12267      * Aborts the executing transaction
12268      */
12269     abort : function(){
12270         if(this.transaction){
12271             Roo.Ajax.abort(this.transaction);
12272         }
12273     },
12274
12275     /**
12276      * Returns true if an update is in progress
12277      * @return {Boolean}
12278      */
12279     isUpdating : function(){
12280         if(this.transaction){
12281             return Roo.Ajax.isLoading(this.transaction);
12282         }
12283         return false;
12284     }
12285 });
12286
12287 /**
12288  * @class Roo.UpdateManager.defaults
12289  * @static (not really - but it helps the doc tool)
12290  * The defaults collection enables customizing the default properties of UpdateManager
12291  */
12292    Roo.UpdateManager.defaults = {
12293        /**
12294          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12295          * @type Number
12296          */
12297          timeout : 30,
12298
12299          /**
12300          * True to process scripts by default (Defaults to false).
12301          * @type Boolean
12302          */
12303         loadScripts : false,
12304
12305         /**
12306         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12307         * @type String
12308         */
12309         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12310         /**
12311          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12312          * @type Boolean
12313          */
12314         disableCaching : false,
12315         /**
12316          * Whether to show indicatorText when loading (Defaults to true).
12317          * @type Boolean
12318          */
12319         showLoadIndicator : true,
12320         /**
12321          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12322          * @type String
12323          */
12324         indicatorText : '<div class="loading-indicator">Loading...</div>'
12325    };
12326
12327 /**
12328  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12329  *Usage:
12330  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12331  * @param {String/HTMLElement/Roo.Element} el The element to update
12332  * @param {String} url The url
12333  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12334  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12335  * @static
12336  * @deprecated
12337  * @member Roo.UpdateManager
12338  */
12339 Roo.UpdateManager.updateElement = function(el, url, params, options){
12340     var um = Roo.get(el, true).getUpdateManager();
12341     Roo.apply(um, options);
12342     um.update(url, params, options ? options.callback : null);
12343 };
12344 // alias for backwards compat
12345 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12346 /**
12347  * @class Roo.UpdateManager.BasicRenderer
12348  * Default Content renderer. Updates the elements innerHTML with the responseText.
12349  */
12350 Roo.UpdateManager.BasicRenderer = function(){};
12351
12352 Roo.UpdateManager.BasicRenderer.prototype = {
12353     /**
12354      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12355      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12356      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12357      * @param {Roo.Element} el The element being rendered
12358      * @param {Object} response The YUI Connect response object
12359      * @param {UpdateManager} updateManager The calling update manager
12360      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12361      */
12362      render : function(el, response, updateManager, callback){
12363         el.update(response.responseText, updateManager.loadScripts, callback);
12364     }
12365 };
12366 /*
12367  * Based on:
12368  * Roo JS
12369  * (c)) Alan Knowles
12370  * Licence : LGPL
12371  */
12372
12373
12374 /**
12375  * @class Roo.DomTemplate
12376  * @extends Roo.Template
12377  * An effort at a dom based template engine..
12378  *
12379  * Similar to XTemplate, except it uses dom parsing to create the template..
12380  *
12381  * Supported features:
12382  *
12383  *  Tags:
12384
12385 <pre><code>
12386       {a_variable} - output encoded.
12387       {a_variable.format:("Y-m-d")} - call a method on the variable
12388       {a_variable:raw} - unencoded output
12389       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12390       {a_variable:this.method_on_template(...)} - call a method on the template object.
12391  
12392 </code></pre>
12393  *  The tpl tag:
12394 <pre><code>
12395         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12396         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12397         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12398         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12399   
12400 </code></pre>
12401  *      
12402  */
12403 Roo.DomTemplate = function()
12404 {
12405      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12406      if (this.html) {
12407         this.compile();
12408      }
12409 };
12410
12411
12412 Roo.extend(Roo.DomTemplate, Roo.Template, {
12413     /**
12414      * id counter for sub templates.
12415      */
12416     id : 0,
12417     /**
12418      * flag to indicate if dom parser is inside a pre,
12419      * it will strip whitespace if not.
12420      */
12421     inPre : false,
12422     
12423     /**
12424      * The various sub templates
12425      */
12426     tpls : false,
12427     
12428     
12429     
12430     /**
12431      *
12432      * basic tag replacing syntax
12433      * WORD:WORD()
12434      *
12435      * // you can fake an object call by doing this
12436      *  x.t:(test,tesT) 
12437      * 
12438      */
12439     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12440     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12441     
12442     iterChild : function (node, method) {
12443         
12444         var oldPre = this.inPre;
12445         if (node.tagName == 'PRE') {
12446             this.inPre = true;
12447         }
12448         for( var i = 0; i < node.childNodes.length; i++) {
12449             method.call(this, node.childNodes[i]);
12450         }
12451         this.inPre = oldPre;
12452     },
12453     
12454     
12455     
12456     /**
12457      * compile the template
12458      *
12459      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12460      *
12461      */
12462     compile: function()
12463     {
12464         var s = this.html;
12465         
12466         // covert the html into DOM...
12467         var doc = false;
12468         var div =false;
12469         try {
12470             doc = document.implementation.createHTMLDocument("");
12471             doc.documentElement.innerHTML =   this.html  ;
12472             div = doc.documentElement;
12473         } catch (e) {
12474             // old IE... - nasty -- it causes all sorts of issues.. with
12475             // images getting pulled from server..
12476             div = document.createElement('div');
12477             div.innerHTML = this.html;
12478         }
12479         //doc.documentElement.innerHTML = htmlBody
12480          
12481         
12482         
12483         this.tpls = [];
12484         var _t = this;
12485         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12486         
12487         var tpls = this.tpls;
12488         
12489         // create a top level template from the snippet..
12490         
12491         //Roo.log(div.innerHTML);
12492         
12493         var tpl = {
12494             uid : 'master',
12495             id : this.id++,
12496             attr : false,
12497             value : false,
12498             body : div.innerHTML,
12499             
12500             forCall : false,
12501             execCall : false,
12502             dom : div,
12503             isTop : true
12504             
12505         };
12506         tpls.unshift(tpl);
12507         
12508         
12509         // compile them...
12510         this.tpls = [];
12511         Roo.each(tpls, function(tp){
12512             this.compileTpl(tp);
12513             this.tpls[tp.id] = tp;
12514         }, this);
12515         
12516         this.master = tpls[0];
12517         return this;
12518         
12519         
12520     },
12521     
12522     compileNode : function(node, istop) {
12523         // test for
12524         //Roo.log(node);
12525         
12526         
12527         // skip anything not a tag..
12528         if (node.nodeType != 1) {
12529             if (node.nodeType == 3 && !this.inPre) {
12530                 // reduce white space..
12531                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12532                 
12533             }
12534             return;
12535         }
12536         
12537         var tpl = {
12538             uid : false,
12539             id : false,
12540             attr : false,
12541             value : false,
12542             body : '',
12543             
12544             forCall : false,
12545             execCall : false,
12546             dom : false,
12547             isTop : istop
12548             
12549             
12550         };
12551         
12552         
12553         switch(true) {
12554             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12555             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12556             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12557             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12558             // no default..
12559         }
12560         
12561         
12562         if (!tpl.attr) {
12563             // just itterate children..
12564             this.iterChild(node,this.compileNode);
12565             return;
12566         }
12567         tpl.uid = this.id++;
12568         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12569         node.removeAttribute('roo-'+ tpl.attr);
12570         if (tpl.attr != 'name') {
12571             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12572             node.parentNode.replaceChild(placeholder,  node);
12573         } else {
12574             
12575             var placeholder =  document.createElement('span');
12576             placeholder.className = 'roo-tpl-' + tpl.value;
12577             node.parentNode.replaceChild(placeholder,  node);
12578         }
12579         
12580         // parent now sees '{domtplXXXX}
12581         this.iterChild(node,this.compileNode);
12582         
12583         // we should now have node body...
12584         var div = document.createElement('div');
12585         div.appendChild(node);
12586         tpl.dom = node;
12587         // this has the unfortunate side effect of converting tagged attributes
12588         // eg. href="{...}" into %7C...%7D
12589         // this has been fixed by searching for those combo's although it's a bit hacky..
12590         
12591         
12592         tpl.body = div.innerHTML;
12593         
12594         
12595          
12596         tpl.id = tpl.uid;
12597         switch(tpl.attr) {
12598             case 'for' :
12599                 switch (tpl.value) {
12600                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12601                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12602                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12603                 }
12604                 break;
12605             
12606             case 'exec':
12607                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12608                 break;
12609             
12610             case 'if':     
12611                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12612                 break;
12613             
12614             case 'name':
12615                 tpl.id  = tpl.value; // replace non characters???
12616                 break;
12617             
12618         }
12619         
12620         
12621         this.tpls.push(tpl);
12622         
12623         
12624         
12625     },
12626     
12627     
12628     
12629     
12630     /**
12631      * Compile a segment of the template into a 'sub-template'
12632      *
12633      * 
12634      * 
12635      *
12636      */
12637     compileTpl : function(tpl)
12638     {
12639         var fm = Roo.util.Format;
12640         var useF = this.disableFormats !== true;
12641         
12642         var sep = Roo.isGecko ? "+\n" : ",\n";
12643         
12644         var undef = function(str) {
12645             Roo.debug && Roo.log("Property not found :"  + str);
12646             return '';
12647         };
12648           
12649         //Roo.log(tpl.body);
12650         
12651         
12652         
12653         var fn = function(m, lbrace, name, format, args)
12654         {
12655             //Roo.log("ARGS");
12656             //Roo.log(arguments);
12657             args = args ? args.replace(/\\'/g,"'") : args;
12658             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12659             if (typeof(format) == 'undefined') {
12660                 format =  'htmlEncode'; 
12661             }
12662             if (format == 'raw' ) {
12663                 format = false;
12664             }
12665             
12666             if(name.substr(0, 6) == 'domtpl'){
12667                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12668             }
12669             
12670             // build an array of options to determine if value is undefined..
12671             
12672             // basically get 'xxxx.yyyy' then do
12673             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12674             //    (function () { Roo.log("Property not found"); return ''; })() :
12675             //    ......
12676             
12677             var udef_ar = [];
12678             var lookfor = '';
12679             Roo.each(name.split('.'), function(st) {
12680                 lookfor += (lookfor.length ? '.': '') + st;
12681                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12682             });
12683             
12684             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12685             
12686             
12687             if(format && useF){
12688                 
12689                 args = args ? ',' + args : "";
12690                  
12691                 if(format.substr(0, 5) != "this."){
12692                     format = "fm." + format + '(';
12693                 }else{
12694                     format = 'this.call("'+ format.substr(5) + '", ';
12695                     args = ", values";
12696                 }
12697                 
12698                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12699             }
12700              
12701             if (args && args.length) {
12702                 // called with xxyx.yuu:(test,test)
12703                 // change to ()
12704                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12705             }
12706             // raw.. - :raw modifier..
12707             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12708             
12709         };
12710         var body;
12711         // branched to use + in gecko and [].join() in others
12712         if(Roo.isGecko){
12713             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12714                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12715                     "';};};";
12716         }else{
12717             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12718             body.push(tpl.body.replace(/(\r\n|\n)/g,
12719                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12720             body.push("'].join('');};};");
12721             body = body.join('');
12722         }
12723         
12724         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12725        
12726         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12727         eval(body);
12728         
12729         return this;
12730     },
12731      
12732     /**
12733      * same as applyTemplate, except it's done to one of the subTemplates
12734      * when using named templates, you can do:
12735      *
12736      * var str = pl.applySubTemplate('your-name', values);
12737      *
12738      * 
12739      * @param {Number} id of the template
12740      * @param {Object} values to apply to template
12741      * @param {Object} parent (normaly the instance of this object)
12742      */
12743     applySubTemplate : function(id, values, parent)
12744     {
12745         
12746         
12747         var t = this.tpls[id];
12748         
12749         
12750         try { 
12751             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12752                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12753                 return '';
12754             }
12755         } catch(e) {
12756             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12757             Roo.log(values);
12758           
12759             return '';
12760         }
12761         try { 
12762             
12763             if(t.execCall && t.execCall.call(this, values, parent)){
12764                 return '';
12765             }
12766         } catch(e) {
12767             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12768             Roo.log(values);
12769             return '';
12770         }
12771         
12772         try {
12773             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12774             parent = t.target ? values : parent;
12775             if(t.forCall && vs instanceof Array){
12776                 var buf = [];
12777                 for(var i = 0, len = vs.length; i < len; i++){
12778                     try {
12779                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12780                     } catch (e) {
12781                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12782                         Roo.log(e.body);
12783                         //Roo.log(t.compiled);
12784                         Roo.log(vs[i]);
12785                     }   
12786                 }
12787                 return buf.join('');
12788             }
12789         } catch (e) {
12790             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12791             Roo.log(values);
12792             return '';
12793         }
12794         try {
12795             return t.compiled.call(this, vs, parent);
12796         } catch (e) {
12797             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12798             Roo.log(e.body);
12799             //Roo.log(t.compiled);
12800             Roo.log(values);
12801             return '';
12802         }
12803     },
12804
12805    
12806
12807     applyTemplate : function(values){
12808         return this.master.compiled.call(this, values, {});
12809         //var s = this.subs;
12810     },
12811
12812     apply : function(){
12813         return this.applyTemplate.apply(this, arguments);
12814     }
12815
12816  });
12817
12818 Roo.DomTemplate.from = function(el){
12819     el = Roo.getDom(el);
12820     return new Roo.Domtemplate(el.value || el.innerHTML);
12821 };/*
12822  * Based on:
12823  * Ext JS Library 1.1.1
12824  * Copyright(c) 2006-2007, Ext JS, LLC.
12825  *
12826  * Originally Released Under LGPL - original licence link has changed is not relivant.
12827  *
12828  * Fork - LGPL
12829  * <script type="text/javascript">
12830  */
12831
12832 /**
12833  * @class Roo.util.DelayedTask
12834  * Provides a convenient method of performing setTimeout where a new
12835  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12836  * You can use this class to buffer
12837  * the keypress events for a certain number of milliseconds, and perform only if they stop
12838  * for that amount of time.
12839  * @constructor The parameters to this constructor serve as defaults and are not required.
12840  * @param {Function} fn (optional) The default function to timeout
12841  * @param {Object} scope (optional) The default scope of that timeout
12842  * @param {Array} args (optional) The default Array of arguments
12843  */
12844 Roo.util.DelayedTask = function(fn, scope, args){
12845     var id = null, d, t;
12846
12847     var call = function(){
12848         var now = new Date().getTime();
12849         if(now - t >= d){
12850             clearInterval(id);
12851             id = null;
12852             fn.apply(scope, args || []);
12853         }
12854     };
12855     /**
12856      * Cancels any pending timeout and queues a new one
12857      * @param {Number} delay The milliseconds to delay
12858      * @param {Function} newFn (optional) Overrides function passed to constructor
12859      * @param {Object} newScope (optional) Overrides scope passed to constructor
12860      * @param {Array} newArgs (optional) Overrides args passed to constructor
12861      */
12862     this.delay = function(delay, newFn, newScope, newArgs){
12863         if(id && delay != d){
12864             this.cancel();
12865         }
12866         d = delay;
12867         t = new Date().getTime();
12868         fn = newFn || fn;
12869         scope = newScope || scope;
12870         args = newArgs || args;
12871         if(!id){
12872             id = setInterval(call, d);
12873         }
12874     };
12875
12876     /**
12877      * Cancel the last queued timeout
12878      */
12879     this.cancel = function(){
12880         if(id){
12881             clearInterval(id);
12882             id = null;
12883         }
12884     };
12885 };/*
12886  * Based on:
12887  * Ext JS Library 1.1.1
12888  * Copyright(c) 2006-2007, Ext JS, LLC.
12889  *
12890  * Originally Released Under LGPL - original licence link has changed is not relivant.
12891  *
12892  * Fork - LGPL
12893  * <script type="text/javascript">
12894  */
12895  
12896  
12897 Roo.util.TaskRunner = function(interval){
12898     interval = interval || 10;
12899     var tasks = [], removeQueue = [];
12900     var id = 0;
12901     var running = false;
12902
12903     var stopThread = function(){
12904         running = false;
12905         clearInterval(id);
12906         id = 0;
12907     };
12908
12909     var startThread = function(){
12910         if(!running){
12911             running = true;
12912             id = setInterval(runTasks, interval);
12913         }
12914     };
12915
12916     var removeTask = function(task){
12917         removeQueue.push(task);
12918         if(task.onStop){
12919             task.onStop();
12920         }
12921     };
12922
12923     var runTasks = function(){
12924         if(removeQueue.length > 0){
12925             for(var i = 0, len = removeQueue.length; i < len; i++){
12926                 tasks.remove(removeQueue[i]);
12927             }
12928             removeQueue = [];
12929             if(tasks.length < 1){
12930                 stopThread();
12931                 return;
12932             }
12933         }
12934         var now = new Date().getTime();
12935         for(var i = 0, len = tasks.length; i < len; ++i){
12936             var t = tasks[i];
12937             var itime = now - t.taskRunTime;
12938             if(t.interval <= itime){
12939                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12940                 t.taskRunTime = now;
12941                 if(rt === false || t.taskRunCount === t.repeat){
12942                     removeTask(t);
12943                     return;
12944                 }
12945             }
12946             if(t.duration && t.duration <= (now - t.taskStartTime)){
12947                 removeTask(t);
12948             }
12949         }
12950     };
12951
12952     /**
12953      * Queues a new task.
12954      * @param {Object} task
12955      */
12956     this.start = function(task){
12957         tasks.push(task);
12958         task.taskStartTime = new Date().getTime();
12959         task.taskRunTime = 0;
12960         task.taskRunCount = 0;
12961         startThread();
12962         return task;
12963     };
12964
12965     this.stop = function(task){
12966         removeTask(task);
12967         return task;
12968     };
12969
12970     this.stopAll = function(){
12971         stopThread();
12972         for(var i = 0, len = tasks.length; i < len; i++){
12973             if(tasks[i].onStop){
12974                 tasks[i].onStop();
12975             }
12976         }
12977         tasks = [];
12978         removeQueue = [];
12979     };
12980 };
12981
12982 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12983  * Based on:
12984  * Ext JS Library 1.1.1
12985  * Copyright(c) 2006-2007, Ext JS, LLC.
12986  *
12987  * Originally Released Under LGPL - original licence link has changed is not relivant.
12988  *
12989  * Fork - LGPL
12990  * <script type="text/javascript">
12991  */
12992
12993  
12994 /**
12995  * @class Roo.util.MixedCollection
12996  * @extends Roo.util.Observable
12997  * A Collection class that maintains both numeric indexes and keys and exposes events.
12998  * @constructor
12999  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13000  * collection (defaults to false)
13001  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13002  * and return the key value for that item.  This is used when available to look up the key on items that
13003  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13004  * equivalent to providing an implementation for the {@link #getKey} method.
13005  */
13006 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13007     this.items = [];
13008     this.map = {};
13009     this.keys = [];
13010     this.length = 0;
13011     this.addEvents({
13012         /**
13013          * @event clear
13014          * Fires when the collection is cleared.
13015          */
13016         "clear" : true,
13017         /**
13018          * @event add
13019          * Fires when an item is added to the collection.
13020          * @param {Number} index The index at which the item was added.
13021          * @param {Object} o The item added.
13022          * @param {String} key The key associated with the added item.
13023          */
13024         "add" : true,
13025         /**
13026          * @event replace
13027          * Fires when an item is replaced in the collection.
13028          * @param {String} key he key associated with the new added.
13029          * @param {Object} old The item being replaced.
13030          * @param {Object} new The new item.
13031          */
13032         "replace" : true,
13033         /**
13034          * @event remove
13035          * Fires when an item is removed from the collection.
13036          * @param {Object} o The item being removed.
13037          * @param {String} key (optional) The key associated with the removed item.
13038          */
13039         "remove" : true,
13040         "sort" : true
13041     });
13042     this.allowFunctions = allowFunctions === true;
13043     if(keyFn){
13044         this.getKey = keyFn;
13045     }
13046     Roo.util.MixedCollection.superclass.constructor.call(this);
13047 };
13048
13049 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13050     allowFunctions : false,
13051     
13052 /**
13053  * Adds an item to the collection.
13054  * @param {String} key The key to associate with the item
13055  * @param {Object} o The item to add.
13056  * @return {Object} The item added.
13057  */
13058     add : function(key, o){
13059         if(arguments.length == 1){
13060             o = arguments[0];
13061             key = this.getKey(o);
13062         }
13063         if(typeof key == "undefined" || key === null){
13064             this.length++;
13065             this.items.push(o);
13066             this.keys.push(null);
13067         }else{
13068             var old = this.map[key];
13069             if(old){
13070                 return this.replace(key, o);
13071             }
13072             this.length++;
13073             this.items.push(o);
13074             this.map[key] = o;
13075             this.keys.push(key);
13076         }
13077         this.fireEvent("add", this.length-1, o, key);
13078         return o;
13079     },
13080        
13081 /**
13082   * MixedCollection has a generic way to fetch keys if you implement getKey.
13083 <pre><code>
13084 // normal way
13085 var mc = new Roo.util.MixedCollection();
13086 mc.add(someEl.dom.id, someEl);
13087 mc.add(otherEl.dom.id, otherEl);
13088 //and so on
13089
13090 // using getKey
13091 var mc = new Roo.util.MixedCollection();
13092 mc.getKey = function(el){
13093    return el.dom.id;
13094 };
13095 mc.add(someEl);
13096 mc.add(otherEl);
13097
13098 // or via the constructor
13099 var mc = new Roo.util.MixedCollection(false, function(el){
13100    return el.dom.id;
13101 });
13102 mc.add(someEl);
13103 mc.add(otherEl);
13104 </code></pre>
13105  * @param o {Object} The item for which to find the key.
13106  * @return {Object} The key for the passed item.
13107  */
13108     getKey : function(o){
13109          return o.id; 
13110     },
13111    
13112 /**
13113  * Replaces an item in the collection.
13114  * @param {String} key The key associated with the item to replace, or the item to replace.
13115  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13116  * @return {Object}  The new item.
13117  */
13118     replace : function(key, o){
13119         if(arguments.length == 1){
13120             o = arguments[0];
13121             key = this.getKey(o);
13122         }
13123         var old = this.item(key);
13124         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13125              return this.add(key, o);
13126         }
13127         var index = this.indexOfKey(key);
13128         this.items[index] = o;
13129         this.map[key] = o;
13130         this.fireEvent("replace", key, old, o);
13131         return o;
13132     },
13133    
13134 /**
13135  * Adds all elements of an Array or an Object to the collection.
13136  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13137  * an Array of values, each of which are added to the collection.
13138  */
13139     addAll : function(objs){
13140         if(arguments.length > 1 || objs instanceof Array){
13141             var args = arguments.length > 1 ? arguments : objs;
13142             for(var i = 0, len = args.length; i < len; i++){
13143                 this.add(args[i]);
13144             }
13145         }else{
13146             for(var key in objs){
13147                 if(this.allowFunctions || typeof objs[key] != "function"){
13148                     this.add(key, objs[key]);
13149                 }
13150             }
13151         }
13152     },
13153    
13154 /**
13155  * Executes the specified function once for every item in the collection, passing each
13156  * item as the first and only parameter. returning false from the function will stop the iteration.
13157  * @param {Function} fn The function to execute for each item.
13158  * @param {Object} scope (optional) The scope in which to execute the function.
13159  */
13160     each : function(fn, scope){
13161         var items = [].concat(this.items); // each safe for removal
13162         for(var i = 0, len = items.length; i < len; i++){
13163             if(fn.call(scope || items[i], items[i], i, len) === false){
13164                 break;
13165             }
13166         }
13167     },
13168    
13169 /**
13170  * Executes the specified function once for every key in the collection, passing each
13171  * key, and its associated item as the first two parameters.
13172  * @param {Function} fn The function to execute for each item.
13173  * @param {Object} scope (optional) The scope in which to execute the function.
13174  */
13175     eachKey : function(fn, scope){
13176         for(var i = 0, len = this.keys.length; i < len; i++){
13177             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13178         }
13179     },
13180    
13181 /**
13182  * Returns the first item in the collection which elicits a true return value from the
13183  * passed selection function.
13184  * @param {Function} fn The selection function to execute for each item.
13185  * @param {Object} scope (optional) The scope in which to execute the function.
13186  * @return {Object} The first item in the collection which returned true from the selection function.
13187  */
13188     find : function(fn, scope){
13189         for(var i = 0, len = this.items.length; i < len; i++){
13190             if(fn.call(scope || window, this.items[i], this.keys[i])){
13191                 return this.items[i];
13192             }
13193         }
13194         return null;
13195     },
13196    
13197 /**
13198  * Inserts an item at the specified index in the collection.
13199  * @param {Number} index The index to insert the item at.
13200  * @param {String} key The key to associate with the new item, or the item itself.
13201  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13202  * @return {Object} The item inserted.
13203  */
13204     insert : function(index, key, o){
13205         if(arguments.length == 2){
13206             o = arguments[1];
13207             key = this.getKey(o);
13208         }
13209         if(index >= this.length){
13210             return this.add(key, o);
13211         }
13212         this.length++;
13213         this.items.splice(index, 0, o);
13214         if(typeof key != "undefined" && key != null){
13215             this.map[key] = o;
13216         }
13217         this.keys.splice(index, 0, key);
13218         this.fireEvent("add", index, o, key);
13219         return o;
13220     },
13221    
13222 /**
13223  * Removed an item from the collection.
13224  * @param {Object} o The item to remove.
13225  * @return {Object} The item removed.
13226  */
13227     remove : function(o){
13228         return this.removeAt(this.indexOf(o));
13229     },
13230    
13231 /**
13232  * Remove an item from a specified index in the collection.
13233  * @param {Number} index The index within the collection of the item to remove.
13234  */
13235     removeAt : function(index){
13236         if(index < this.length && index >= 0){
13237             this.length--;
13238             var o = this.items[index];
13239             this.items.splice(index, 1);
13240             var key = this.keys[index];
13241             if(typeof key != "undefined"){
13242                 delete this.map[key];
13243             }
13244             this.keys.splice(index, 1);
13245             this.fireEvent("remove", o, key);
13246         }
13247     },
13248    
13249 /**
13250  * Removed an item associated with the passed key fom the collection.
13251  * @param {String} key The key of the item to remove.
13252  */
13253     removeKey : function(key){
13254         return this.removeAt(this.indexOfKey(key));
13255     },
13256    
13257 /**
13258  * Returns the number of items in the collection.
13259  * @return {Number} the number of items in the collection.
13260  */
13261     getCount : function(){
13262         return this.length; 
13263     },
13264    
13265 /**
13266  * Returns index within the collection of the passed Object.
13267  * @param {Object} o The item to find the index of.
13268  * @return {Number} index of the item.
13269  */
13270     indexOf : function(o){
13271         if(!this.items.indexOf){
13272             for(var i = 0, len = this.items.length; i < len; i++){
13273                 if(this.items[i] == o) {
13274                     return i;
13275                 }
13276             }
13277             return -1;
13278         }else{
13279             return this.items.indexOf(o);
13280         }
13281     },
13282    
13283 /**
13284  * Returns index within the collection of the passed key.
13285  * @param {String} key The key to find the index of.
13286  * @return {Number} index of the key.
13287  */
13288     indexOfKey : function(key){
13289         if(!this.keys.indexOf){
13290             for(var i = 0, len = this.keys.length; i < len; i++){
13291                 if(this.keys[i] == key) {
13292                     return i;
13293                 }
13294             }
13295             return -1;
13296         }else{
13297             return this.keys.indexOf(key);
13298         }
13299     },
13300    
13301 /**
13302  * Returns the item associated with the passed key OR index. Key has priority over index.
13303  * @param {String/Number} key The key or index of the item.
13304  * @return {Object} The item associated with the passed key.
13305  */
13306     item : function(key){
13307         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13308         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13309     },
13310     
13311 /**
13312  * Returns the item at the specified index.
13313  * @param {Number} index The index of the item.
13314  * @return {Object}
13315  */
13316     itemAt : function(index){
13317         return this.items[index];
13318     },
13319     
13320 /**
13321  * Returns the item associated with the passed key.
13322  * @param {String/Number} key The key of the item.
13323  * @return {Object} The item associated with the passed key.
13324  */
13325     key : function(key){
13326         return this.map[key];
13327     },
13328    
13329 /**
13330  * Returns true if the collection contains the passed Object as an item.
13331  * @param {Object} o  The Object to look for in the collection.
13332  * @return {Boolean} True if the collection contains the Object as an item.
13333  */
13334     contains : function(o){
13335         return this.indexOf(o) != -1;
13336     },
13337    
13338 /**
13339  * Returns true if the collection contains the passed Object as a key.
13340  * @param {String} key The key to look for in the collection.
13341  * @return {Boolean} True if the collection contains the Object as a key.
13342  */
13343     containsKey : function(key){
13344         return typeof this.map[key] != "undefined";
13345     },
13346    
13347 /**
13348  * Removes all items from the collection.
13349  */
13350     clear : function(){
13351         this.length = 0;
13352         this.items = [];
13353         this.keys = [];
13354         this.map = {};
13355         this.fireEvent("clear");
13356     },
13357    
13358 /**
13359  * Returns the first item in the collection.
13360  * @return {Object} the first item in the collection..
13361  */
13362     first : function(){
13363         return this.items[0]; 
13364     },
13365    
13366 /**
13367  * Returns the last item in the collection.
13368  * @return {Object} the last item in the collection..
13369  */
13370     last : function(){
13371         return this.items[this.length-1];   
13372     },
13373     
13374     _sort : function(property, dir, fn){
13375         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13376         fn = fn || function(a, b){
13377             return a-b;
13378         };
13379         var c = [], k = this.keys, items = this.items;
13380         for(var i = 0, len = items.length; i < len; i++){
13381             c[c.length] = {key: k[i], value: items[i], index: i};
13382         }
13383         c.sort(function(a, b){
13384             var v = fn(a[property], b[property]) * dsc;
13385             if(v == 0){
13386                 v = (a.index < b.index ? -1 : 1);
13387             }
13388             return v;
13389         });
13390         for(var i = 0, len = c.length; i < len; i++){
13391             items[i] = c[i].value;
13392             k[i] = c[i].key;
13393         }
13394         this.fireEvent("sort", this);
13395     },
13396     
13397     /**
13398      * Sorts this collection with the passed comparison function
13399      * @param {String} direction (optional) "ASC" or "DESC"
13400      * @param {Function} fn (optional) comparison function
13401      */
13402     sort : function(dir, fn){
13403         this._sort("value", dir, fn);
13404     },
13405     
13406     /**
13407      * Sorts this collection by keys
13408      * @param {String} direction (optional) "ASC" or "DESC"
13409      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13410      */
13411     keySort : function(dir, fn){
13412         this._sort("key", dir, fn || function(a, b){
13413             return String(a).toUpperCase()-String(b).toUpperCase();
13414         });
13415     },
13416     
13417     /**
13418      * Returns a range of items in this collection
13419      * @param {Number} startIndex (optional) defaults to 0
13420      * @param {Number} endIndex (optional) default to the last item
13421      * @return {Array} An array of items
13422      */
13423     getRange : function(start, end){
13424         var items = this.items;
13425         if(items.length < 1){
13426             return [];
13427         }
13428         start = start || 0;
13429         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13430         var r = [];
13431         if(start <= end){
13432             for(var i = start; i <= end; i++) {
13433                     r[r.length] = items[i];
13434             }
13435         }else{
13436             for(var i = start; i >= end; i--) {
13437                     r[r.length] = items[i];
13438             }
13439         }
13440         return r;
13441     },
13442         
13443     /**
13444      * Filter the <i>objects</i> in this collection by a specific property. 
13445      * Returns a new collection that has been filtered.
13446      * @param {String} property A property on your objects
13447      * @param {String/RegExp} value Either string that the property values 
13448      * should start with or a RegExp to test against the property
13449      * @return {MixedCollection} The new filtered collection
13450      */
13451     filter : function(property, value){
13452         if(!value.exec){ // not a regex
13453             value = String(value);
13454             if(value.length == 0){
13455                 return this.clone();
13456             }
13457             value = new RegExp("^" + Roo.escapeRe(value), "i");
13458         }
13459         return this.filterBy(function(o){
13460             return o && value.test(o[property]);
13461         });
13462         },
13463     
13464     /**
13465      * Filter by a function. * Returns a new collection that has been filtered.
13466      * The passed function will be called with each 
13467      * object in the collection. If the function returns true, the value is included 
13468      * otherwise it is filtered.
13469      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13470      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13471      * @return {MixedCollection} The new filtered collection
13472      */
13473     filterBy : function(fn, scope){
13474         var r = new Roo.util.MixedCollection();
13475         r.getKey = this.getKey;
13476         var k = this.keys, it = this.items;
13477         for(var i = 0, len = it.length; i < len; i++){
13478             if(fn.call(scope||this, it[i], k[i])){
13479                                 r.add(k[i], it[i]);
13480                         }
13481         }
13482         return r;
13483     },
13484     
13485     /**
13486      * Creates a duplicate of this collection
13487      * @return {MixedCollection}
13488      */
13489     clone : function(){
13490         var r = new Roo.util.MixedCollection();
13491         var k = this.keys, it = this.items;
13492         for(var i = 0, len = it.length; i < len; i++){
13493             r.add(k[i], it[i]);
13494         }
13495         r.getKey = this.getKey;
13496         return r;
13497     }
13498 });
13499 /**
13500  * Returns the item associated with the passed key or index.
13501  * @method
13502  * @param {String/Number} key The key or index of the item.
13503  * @return {Object} The item associated with the passed key.
13504  */
13505 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13506  * Based on:
13507  * Ext JS Library 1.1.1
13508  * Copyright(c) 2006-2007, Ext JS, LLC.
13509  *
13510  * Originally Released Under LGPL - original licence link has changed is not relivant.
13511  *
13512  * Fork - LGPL
13513  * <script type="text/javascript">
13514  */
13515 /**
13516  * @class Roo.util.JSON
13517  * Modified version of Douglas Crockford"s json.js that doesn"t
13518  * mess with the Object prototype 
13519  * http://www.json.org/js.html
13520  * @singleton
13521  */
13522 Roo.util.JSON = new (function(){
13523     var useHasOwn = {}.hasOwnProperty ? true : false;
13524     
13525     // crashes Safari in some instances
13526     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13527     
13528     var pad = function(n) {
13529         return n < 10 ? "0" + n : n;
13530     };
13531     
13532     var m = {
13533         "\b": '\\b',
13534         "\t": '\\t',
13535         "\n": '\\n',
13536         "\f": '\\f',
13537         "\r": '\\r',
13538         '"' : '\\"',
13539         "\\": '\\\\'
13540     };
13541
13542     var encodeString = function(s){
13543         if (/["\\\x00-\x1f]/.test(s)) {
13544             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13545                 var c = m[b];
13546                 if(c){
13547                     return c;
13548                 }
13549                 c = b.charCodeAt();
13550                 return "\\u00" +
13551                     Math.floor(c / 16).toString(16) +
13552                     (c % 16).toString(16);
13553             }) + '"';
13554         }
13555         return '"' + s + '"';
13556     };
13557     
13558     var encodeArray = function(o){
13559         var a = ["["], b, i, l = o.length, v;
13560             for (i = 0; i < l; i += 1) {
13561                 v = o[i];
13562                 switch (typeof v) {
13563                     case "undefined":
13564                     case "function":
13565                     case "unknown":
13566                         break;
13567                     default:
13568                         if (b) {
13569                             a.push(',');
13570                         }
13571                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13572                         b = true;
13573                 }
13574             }
13575             a.push("]");
13576             return a.join("");
13577     };
13578     
13579     var encodeDate = function(o){
13580         return '"' + o.getFullYear() + "-" +
13581                 pad(o.getMonth() + 1) + "-" +
13582                 pad(o.getDate()) + "T" +
13583                 pad(o.getHours()) + ":" +
13584                 pad(o.getMinutes()) + ":" +
13585                 pad(o.getSeconds()) + '"';
13586     };
13587     
13588     /**
13589      * Encodes an Object, Array or other value
13590      * @param {Mixed} o The variable to encode
13591      * @return {String} The JSON string
13592      */
13593     this.encode = function(o)
13594     {
13595         // should this be extended to fully wrap stringify..
13596         
13597         if(typeof o == "undefined" || o === null){
13598             return "null";
13599         }else if(o instanceof Array){
13600             return encodeArray(o);
13601         }else if(o instanceof Date){
13602             return encodeDate(o);
13603         }else if(typeof o == "string"){
13604             return encodeString(o);
13605         }else if(typeof o == "number"){
13606             return isFinite(o) ? String(o) : "null";
13607         }else if(typeof o == "boolean"){
13608             return String(o);
13609         }else {
13610             var a = ["{"], b, i, v;
13611             for (i in o) {
13612                 if(!useHasOwn || o.hasOwnProperty(i)) {
13613                     v = o[i];
13614                     switch (typeof v) {
13615                     case "undefined":
13616                     case "function":
13617                     case "unknown":
13618                         break;
13619                     default:
13620                         if(b){
13621                             a.push(',');
13622                         }
13623                         a.push(this.encode(i), ":",
13624                                 v === null ? "null" : this.encode(v));
13625                         b = true;
13626                     }
13627                 }
13628             }
13629             a.push("}");
13630             return a.join("");
13631         }
13632     };
13633     
13634     /**
13635      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13636      * @param {String} json The JSON string
13637      * @return {Object} The resulting object
13638      */
13639     this.decode = function(json){
13640         
13641         return  /** eval:var:json */ eval("(" + json + ')');
13642     };
13643 })();
13644 /** 
13645  * Shorthand for {@link Roo.util.JSON#encode}
13646  * @member Roo encode 
13647  * @method */
13648 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13649 /** 
13650  * Shorthand for {@link Roo.util.JSON#decode}
13651  * @member Roo decode 
13652  * @method */
13653 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13654 /*
13655  * Based on:
13656  * Ext JS Library 1.1.1
13657  * Copyright(c) 2006-2007, Ext JS, LLC.
13658  *
13659  * Originally Released Under LGPL - original licence link has changed is not relivant.
13660  *
13661  * Fork - LGPL
13662  * <script type="text/javascript">
13663  */
13664  
13665 /**
13666  * @class Roo.util.Format
13667  * Reusable data formatting functions
13668  * @singleton
13669  */
13670 Roo.util.Format = function(){
13671     var trimRe = /^\s+|\s+$/g;
13672     return {
13673         /**
13674          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13675          * @param {String} value The string to truncate
13676          * @param {Number} length The maximum length to allow before truncating
13677          * @return {String} The converted text
13678          */
13679         ellipsis : function(value, len){
13680             if(value && value.length > len){
13681                 return value.substr(0, len-3)+"...";
13682             }
13683             return value;
13684         },
13685
13686         /**
13687          * Checks a reference and converts it to empty string if it is undefined
13688          * @param {Mixed} value Reference to check
13689          * @return {Mixed} Empty string if converted, otherwise the original value
13690          */
13691         undef : function(value){
13692             return typeof value != "undefined" ? value : "";
13693         },
13694
13695         /**
13696          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13697          * @param {String} value The string to encode
13698          * @return {String} The encoded text
13699          */
13700         htmlEncode : function(value){
13701             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13702         },
13703
13704         /**
13705          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13706          * @param {String} value The string to decode
13707          * @return {String} The decoded text
13708          */
13709         htmlDecode : function(value){
13710             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13711         },
13712
13713         /**
13714          * Trims any whitespace from either side of a string
13715          * @param {String} value The text to trim
13716          * @return {String} The trimmed text
13717          */
13718         trim : function(value){
13719             return String(value).replace(trimRe, "");
13720         },
13721
13722         /**
13723          * Returns a substring from within an original string
13724          * @param {String} value The original text
13725          * @param {Number} start The start index of the substring
13726          * @param {Number} length The length of the substring
13727          * @return {String} The substring
13728          */
13729         substr : function(value, start, length){
13730             return String(value).substr(start, length);
13731         },
13732
13733         /**
13734          * Converts a string to all lower case letters
13735          * @param {String} value The text to convert
13736          * @return {String} The converted text
13737          */
13738         lowercase : function(value){
13739             return String(value).toLowerCase();
13740         },
13741
13742         /**
13743          * Converts a string to all upper case letters
13744          * @param {String} value The text to convert
13745          * @return {String} The converted text
13746          */
13747         uppercase : function(value){
13748             return String(value).toUpperCase();
13749         },
13750
13751         /**
13752          * Converts the first character only of a string to upper case
13753          * @param {String} value The text to convert
13754          * @return {String} The converted text
13755          */
13756         capitalize : function(value){
13757             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13758         },
13759
13760         // private
13761         call : function(value, fn){
13762             if(arguments.length > 2){
13763                 var args = Array.prototype.slice.call(arguments, 2);
13764                 args.unshift(value);
13765                  
13766                 return /** eval:var:value */  eval(fn).apply(window, args);
13767             }else{
13768                 /** eval:var:value */
13769                 return /** eval:var:value */ eval(fn).call(window, value);
13770             }
13771         },
13772
13773        
13774         /**
13775          * safer version of Math.toFixed..??/
13776          * @param {Number/String} value The numeric value to format
13777          * @param {Number/String} value Decimal places 
13778          * @return {String} The formatted currency string
13779          */
13780         toFixed : function(v, n)
13781         {
13782             // why not use to fixed - precision is buggered???
13783             if (!n) {
13784                 return Math.round(v-0);
13785             }
13786             var fact = Math.pow(10,n+1);
13787             v = (Math.round((v-0)*fact))/fact;
13788             var z = (''+fact).substring(2);
13789             if (v == Math.floor(v)) {
13790                 return Math.floor(v) + '.' + z;
13791             }
13792             
13793             // now just padd decimals..
13794             var ps = String(v).split('.');
13795             var fd = (ps[1] + z);
13796             var r = fd.substring(0,n); 
13797             var rm = fd.substring(n); 
13798             if (rm < 5) {
13799                 return ps[0] + '.' + r;
13800             }
13801             r*=1; // turn it into a number;
13802             r++;
13803             if (String(r).length != n) {
13804                 ps[0]*=1;
13805                 ps[0]++;
13806                 r = String(r).substring(1); // chop the end off.
13807             }
13808             
13809             return ps[0] + '.' + r;
13810              
13811         },
13812         
13813         /**
13814          * Format a number as US currency
13815          * @param {Number/String} value The numeric value to format
13816          * @return {String} The formatted currency string
13817          */
13818         usMoney : function(v){
13819             return '$' + Roo.util.Format.number(v);
13820         },
13821         
13822         /**
13823          * Format a number
13824          * eventually this should probably emulate php's number_format
13825          * @param {Number/String} value The numeric value to format
13826          * @param {Number} decimals number of decimal places
13827          * @param {String} delimiter for thousands (default comma)
13828          * @return {String} The formatted currency string
13829          */
13830         number : function(v, decimals, thousandsDelimiter)
13831         {
13832             // multiply and round.
13833             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13834             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13835             
13836             var mul = Math.pow(10, decimals);
13837             var zero = String(mul).substring(1);
13838             v = (Math.round((v-0)*mul))/mul;
13839             
13840             // if it's '0' number.. then
13841             
13842             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13843             v = String(v);
13844             var ps = v.split('.');
13845             var whole = ps[0];
13846             
13847             var r = /(\d+)(\d{3})/;
13848             // add comma's
13849             
13850             if(thousandsDelimiter.length != 0) {
13851                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13852             } 
13853             
13854             var sub = ps[1] ?
13855                     // has decimals..
13856                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13857                     // does not have decimals
13858                     (decimals ? ('.' + zero) : '');
13859             
13860             
13861             return whole + sub ;
13862         },
13863         
13864         /**
13865          * Parse a value into a formatted date using the specified format pattern.
13866          * @param {Mixed} value The value to format
13867          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13868          * @return {String} The formatted date string
13869          */
13870         date : function(v, format){
13871             if(!v){
13872                 return "";
13873             }
13874             if(!(v instanceof Date)){
13875                 v = new Date(Date.parse(v));
13876             }
13877             return v.dateFormat(format || Roo.util.Format.defaults.date);
13878         },
13879
13880         /**
13881          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13882          * @param {String} format Any valid date format string
13883          * @return {Function} The date formatting function
13884          */
13885         dateRenderer : function(format){
13886             return function(v){
13887                 return Roo.util.Format.date(v, format);  
13888             };
13889         },
13890
13891         // private
13892         stripTagsRE : /<\/?[^>]+>/gi,
13893         
13894         /**
13895          * Strips all HTML tags
13896          * @param {Mixed} value The text from which to strip tags
13897          * @return {String} The stripped text
13898          */
13899         stripTags : function(v){
13900             return !v ? v : String(v).replace(this.stripTagsRE, "");
13901         }
13902     };
13903 }();
13904 Roo.util.Format.defaults = {
13905     date : 'd/M/Y'
13906 };/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917
13918  
13919
13920 /**
13921  * @class Roo.MasterTemplate
13922  * @extends Roo.Template
13923  * Provides a template that can have child templates. The syntax is:
13924 <pre><code>
13925 var t = new Roo.MasterTemplate(
13926         '&lt;select name="{name}"&gt;',
13927                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13928         '&lt;/select&gt;'
13929 );
13930 t.add('options', {value: 'foo', text: 'bar'});
13931 // or you can add multiple child elements in one shot
13932 t.addAll('options', [
13933     {value: 'foo', text: 'bar'},
13934     {value: 'foo2', text: 'bar2'},
13935     {value: 'foo3', text: 'bar3'}
13936 ]);
13937 // then append, applying the master template values
13938 t.append('my-form', {name: 'my-select'});
13939 </code></pre>
13940 * A name attribute for the child template is not required if you have only one child
13941 * template or you want to refer to them by index.
13942  */
13943 Roo.MasterTemplate = function(){
13944     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13945     this.originalHtml = this.html;
13946     var st = {};
13947     var m, re = this.subTemplateRe;
13948     re.lastIndex = 0;
13949     var subIndex = 0;
13950     while(m = re.exec(this.html)){
13951         var name = m[1], content = m[2];
13952         st[subIndex] = {
13953             name: name,
13954             index: subIndex,
13955             buffer: [],
13956             tpl : new Roo.Template(content)
13957         };
13958         if(name){
13959             st[name] = st[subIndex];
13960         }
13961         st[subIndex].tpl.compile();
13962         st[subIndex].tpl.call = this.call.createDelegate(this);
13963         subIndex++;
13964     }
13965     this.subCount = subIndex;
13966     this.subs = st;
13967 };
13968 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13969     /**
13970     * The regular expression used to match sub templates
13971     * @type RegExp
13972     * @property
13973     */
13974     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13975
13976     /**
13977      * Applies the passed values to a child template.
13978      * @param {String/Number} name (optional) The name or index of the child template
13979      * @param {Array/Object} values The values to be applied to the template
13980      * @return {MasterTemplate} this
13981      */
13982      add : function(name, values){
13983         if(arguments.length == 1){
13984             values = arguments[0];
13985             name = 0;
13986         }
13987         var s = this.subs[name];
13988         s.buffer[s.buffer.length] = s.tpl.apply(values);
13989         return this;
13990     },
13991
13992     /**
13993      * Applies all the passed values to a child template.
13994      * @param {String/Number} name (optional) The name or index of the child template
13995      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13996      * @param {Boolean} reset (optional) True to reset the template first
13997      * @return {MasterTemplate} this
13998      */
13999     fill : function(name, values, reset){
14000         var a = arguments;
14001         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14002             values = a[0];
14003             name = 0;
14004             reset = a[1];
14005         }
14006         if(reset){
14007             this.reset();
14008         }
14009         for(var i = 0, len = values.length; i < len; i++){
14010             this.add(name, values[i]);
14011         }
14012         return this;
14013     },
14014
14015     /**
14016      * Resets the template for reuse
14017      * @return {MasterTemplate} this
14018      */
14019      reset : function(){
14020         var s = this.subs;
14021         for(var i = 0; i < this.subCount; i++){
14022             s[i].buffer = [];
14023         }
14024         return this;
14025     },
14026
14027     applyTemplate : function(values){
14028         var s = this.subs;
14029         var replaceIndex = -1;
14030         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14031             return s[++replaceIndex].buffer.join("");
14032         });
14033         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14034     },
14035
14036     apply : function(){
14037         return this.applyTemplate.apply(this, arguments);
14038     },
14039
14040     compile : function(){return this;}
14041 });
14042
14043 /**
14044  * Alias for fill().
14045  * @method
14046  */
14047 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14048  /**
14049  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14050  * var tpl = Roo.MasterTemplate.from('element-id');
14051  * @param {String/HTMLElement} el
14052  * @param {Object} config
14053  * @static
14054  */
14055 Roo.MasterTemplate.from = function(el, config){
14056     el = Roo.getDom(el);
14057     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14058 };/*
14059  * Based on:
14060  * Ext JS Library 1.1.1
14061  * Copyright(c) 2006-2007, Ext JS, LLC.
14062  *
14063  * Originally Released Under LGPL - original licence link has changed is not relivant.
14064  *
14065  * Fork - LGPL
14066  * <script type="text/javascript">
14067  */
14068
14069  
14070 /**
14071  * @class Roo.util.CSS
14072  * Utility class for manipulating CSS rules
14073  * @singleton
14074  */
14075 Roo.util.CSS = function(){
14076         var rules = null;
14077         var doc = document;
14078
14079     var camelRe = /(-[a-z])/gi;
14080     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14081
14082    return {
14083    /**
14084     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14085     * tag and appended to the HEAD of the document.
14086     * @param {String|Object} cssText The text containing the css rules
14087     * @param {String} id An id to add to the stylesheet for later removal
14088     * @return {StyleSheet}
14089     */
14090     createStyleSheet : function(cssText, id){
14091         var ss;
14092         var head = doc.getElementsByTagName("head")[0];
14093         var nrules = doc.createElement("style");
14094         nrules.setAttribute("type", "text/css");
14095         if(id){
14096             nrules.setAttribute("id", id);
14097         }
14098         if (typeof(cssText) != 'string') {
14099             // support object maps..
14100             // not sure if this a good idea.. 
14101             // perhaps it should be merged with the general css handling
14102             // and handle js style props.
14103             var cssTextNew = [];
14104             for(var n in cssText) {
14105                 var citems = [];
14106                 for(var k in cssText[n]) {
14107                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14108                 }
14109                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14110                 
14111             }
14112             cssText = cssTextNew.join("\n");
14113             
14114         }
14115        
14116        
14117        if(Roo.isIE){
14118            head.appendChild(nrules);
14119            ss = nrules.styleSheet;
14120            ss.cssText = cssText;
14121        }else{
14122            try{
14123                 nrules.appendChild(doc.createTextNode(cssText));
14124            }catch(e){
14125                nrules.cssText = cssText; 
14126            }
14127            head.appendChild(nrules);
14128            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14129        }
14130        this.cacheStyleSheet(ss);
14131        return ss;
14132    },
14133
14134    /**
14135     * Removes a style or link tag by id
14136     * @param {String} id The id of the tag
14137     */
14138    removeStyleSheet : function(id){
14139        var existing = doc.getElementById(id);
14140        if(existing){
14141            existing.parentNode.removeChild(existing);
14142        }
14143    },
14144
14145    /**
14146     * Dynamically swaps an existing stylesheet reference for a new one
14147     * @param {String} id The id of an existing link tag to remove
14148     * @param {String} url The href of the new stylesheet to include
14149     */
14150    swapStyleSheet : function(id, url){
14151        this.removeStyleSheet(id);
14152        var ss = doc.createElement("link");
14153        ss.setAttribute("rel", "stylesheet");
14154        ss.setAttribute("type", "text/css");
14155        ss.setAttribute("id", id);
14156        ss.setAttribute("href", url);
14157        doc.getElementsByTagName("head")[0].appendChild(ss);
14158    },
14159    
14160    /**
14161     * Refresh the rule cache if you have dynamically added stylesheets
14162     * @return {Object} An object (hash) of rules indexed by selector
14163     */
14164    refreshCache : function(){
14165        return this.getRules(true);
14166    },
14167
14168    // private
14169    cacheStyleSheet : function(stylesheet){
14170        if(!rules){
14171            rules = {};
14172        }
14173        try{// try catch for cross domain access issue
14174            var ssRules = stylesheet.cssRules || stylesheet.rules;
14175            for(var j = ssRules.length-1; j >= 0; --j){
14176                rules[ssRules[j].selectorText] = ssRules[j];
14177            }
14178        }catch(e){}
14179    },
14180    
14181    /**
14182     * Gets all css rules for the document
14183     * @param {Boolean} refreshCache true to refresh the internal cache
14184     * @return {Object} An object (hash) of rules indexed by selector
14185     */
14186    getRules : function(refreshCache){
14187                 if(rules == null || refreshCache){
14188                         rules = {};
14189                         var ds = doc.styleSheets;
14190                         for(var i =0, len = ds.length; i < len; i++){
14191                             try{
14192                         this.cacheStyleSheet(ds[i]);
14193                     }catch(e){} 
14194                 }
14195                 }
14196                 return rules;
14197         },
14198         
14199         /**
14200     * Gets an an individual CSS rule by selector(s)
14201     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14202     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14203     * @return {CSSRule} The CSS rule or null if one is not found
14204     */
14205    getRule : function(selector, refreshCache){
14206                 var rs = this.getRules(refreshCache);
14207                 if(!(selector instanceof Array)){
14208                     return rs[selector];
14209                 }
14210                 for(var i = 0; i < selector.length; i++){
14211                         if(rs[selector[i]]){
14212                                 return rs[selector[i]];
14213                         }
14214                 }
14215                 return null;
14216         },
14217         
14218         
14219         /**
14220     * Updates a rule property
14221     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14222     * @param {String} property The css property
14223     * @param {String} value The new value for the property
14224     * @return {Boolean} true If a rule was found and updated
14225     */
14226    updateRule : function(selector, property, value){
14227                 if(!(selector instanceof Array)){
14228                         var rule = this.getRule(selector);
14229                         if(rule){
14230                                 rule.style[property.replace(camelRe, camelFn)] = value;
14231                                 return true;
14232                         }
14233                 }else{
14234                         for(var i = 0; i < selector.length; i++){
14235                                 if(this.updateRule(selector[i], property, value)){
14236                                         return true;
14237                                 }
14238                         }
14239                 }
14240                 return false;
14241         }
14242    };   
14243 }();/*
14244  * Based on:
14245  * Ext JS Library 1.1.1
14246  * Copyright(c) 2006-2007, Ext JS, LLC.
14247  *
14248  * Originally Released Under LGPL - original licence link has changed is not relivant.
14249  *
14250  * Fork - LGPL
14251  * <script type="text/javascript">
14252  */
14253
14254  
14255
14256 /**
14257  * @class Roo.util.ClickRepeater
14258  * @extends Roo.util.Observable
14259  * 
14260  * A wrapper class which can be applied to any element. Fires a "click" event while the
14261  * mouse is pressed. The interval between firings may be specified in the config but
14262  * defaults to 10 milliseconds.
14263  * 
14264  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14265  * 
14266  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14267  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14268  * Similar to an autorepeat key delay.
14269  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14270  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14271  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14272  *           "interval" and "delay" are ignored. "immediate" is honored.
14273  * @cfg {Boolean} preventDefault True to prevent the default click event
14274  * @cfg {Boolean} stopDefault True to stop the default click event
14275  * 
14276  * @history
14277  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14278  *     2007-02-02 jvs Renamed to ClickRepeater
14279  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14280  *
14281  *  @constructor
14282  * @param {String/HTMLElement/Element} el The element to listen on
14283  * @param {Object} config
14284  **/
14285 Roo.util.ClickRepeater = function(el, config)
14286 {
14287     this.el = Roo.get(el);
14288     this.el.unselectable();
14289
14290     Roo.apply(this, config);
14291
14292     this.addEvents({
14293     /**
14294      * @event mousedown
14295      * Fires when the mouse button is depressed.
14296      * @param {Roo.util.ClickRepeater} this
14297      */
14298         "mousedown" : true,
14299     /**
14300      * @event click
14301      * Fires on a specified interval during the time the element is pressed.
14302      * @param {Roo.util.ClickRepeater} this
14303      */
14304         "click" : true,
14305     /**
14306      * @event mouseup
14307      * Fires when the mouse key is released.
14308      * @param {Roo.util.ClickRepeater} this
14309      */
14310         "mouseup" : true
14311     });
14312
14313     this.el.on("mousedown", this.handleMouseDown, this);
14314     if(this.preventDefault || this.stopDefault){
14315         this.el.on("click", function(e){
14316             if(this.preventDefault){
14317                 e.preventDefault();
14318             }
14319             if(this.stopDefault){
14320                 e.stopEvent();
14321             }
14322         }, this);
14323     }
14324
14325     // allow inline handler
14326     if(this.handler){
14327         this.on("click", this.handler,  this.scope || this);
14328     }
14329
14330     Roo.util.ClickRepeater.superclass.constructor.call(this);
14331 };
14332
14333 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14334     interval : 20,
14335     delay: 250,
14336     preventDefault : true,
14337     stopDefault : false,
14338     timer : 0,
14339
14340     // private
14341     handleMouseDown : function(){
14342         clearTimeout(this.timer);
14343         this.el.blur();
14344         if(this.pressClass){
14345             this.el.addClass(this.pressClass);
14346         }
14347         this.mousedownTime = new Date();
14348
14349         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14350         this.el.on("mouseout", this.handleMouseOut, this);
14351
14352         this.fireEvent("mousedown", this);
14353         this.fireEvent("click", this);
14354         
14355         this.timer = this.click.defer(this.delay || this.interval, this);
14356     },
14357
14358     // private
14359     click : function(){
14360         this.fireEvent("click", this);
14361         this.timer = this.click.defer(this.getInterval(), this);
14362     },
14363
14364     // private
14365     getInterval: function(){
14366         if(!this.accelerate){
14367             return this.interval;
14368         }
14369         var pressTime = this.mousedownTime.getElapsed();
14370         if(pressTime < 500){
14371             return 400;
14372         }else if(pressTime < 1700){
14373             return 320;
14374         }else if(pressTime < 2600){
14375             return 250;
14376         }else if(pressTime < 3500){
14377             return 180;
14378         }else if(pressTime < 4400){
14379             return 140;
14380         }else if(pressTime < 5300){
14381             return 80;
14382         }else if(pressTime < 6200){
14383             return 50;
14384         }else{
14385             return 10;
14386         }
14387     },
14388
14389     // private
14390     handleMouseOut : function(){
14391         clearTimeout(this.timer);
14392         if(this.pressClass){
14393             this.el.removeClass(this.pressClass);
14394         }
14395         this.el.on("mouseover", this.handleMouseReturn, this);
14396     },
14397
14398     // private
14399     handleMouseReturn : function(){
14400         this.el.un("mouseover", this.handleMouseReturn);
14401         if(this.pressClass){
14402             this.el.addClass(this.pressClass);
14403         }
14404         this.click();
14405     },
14406
14407     // private
14408     handleMouseUp : function(){
14409         clearTimeout(this.timer);
14410         this.el.un("mouseover", this.handleMouseReturn);
14411         this.el.un("mouseout", this.handleMouseOut);
14412         Roo.get(document).un("mouseup", this.handleMouseUp);
14413         this.el.removeClass(this.pressClass);
14414         this.fireEvent("mouseup", this);
14415     }
14416 });/*
14417  * Based on:
14418  * Ext JS Library 1.1.1
14419  * Copyright(c) 2006-2007, Ext JS, LLC.
14420  *
14421  * Originally Released Under LGPL - original licence link has changed is not relivant.
14422  *
14423  * Fork - LGPL
14424  * <script type="text/javascript">
14425  */
14426
14427  
14428 /**
14429  * @class Roo.KeyNav
14430  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14431  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14432  * way to implement custom navigation schemes for any UI component.</p>
14433  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14434  * pageUp, pageDown, del, home, end.  Usage:</p>
14435  <pre><code>
14436 var nav = new Roo.KeyNav("my-element", {
14437     "left" : function(e){
14438         this.moveLeft(e.ctrlKey);
14439     },
14440     "right" : function(e){
14441         this.moveRight(e.ctrlKey);
14442     },
14443     "enter" : function(e){
14444         this.save();
14445     },
14446     scope : this
14447 });
14448 </code></pre>
14449  * @constructor
14450  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14451  * @param {Object} config The config
14452  */
14453 Roo.KeyNav = function(el, config){
14454     this.el = Roo.get(el);
14455     Roo.apply(this, config);
14456     if(!this.disabled){
14457         this.disabled = true;
14458         this.enable();
14459     }
14460 };
14461
14462 Roo.KeyNav.prototype = {
14463     /**
14464      * @cfg {Boolean} disabled
14465      * True to disable this KeyNav instance (defaults to false)
14466      */
14467     disabled : false,
14468     /**
14469      * @cfg {String} defaultEventAction
14470      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14471      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14472      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14473      */
14474     defaultEventAction: "stopEvent",
14475     /**
14476      * @cfg {Boolean} forceKeyDown
14477      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14478      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14479      * handle keydown instead of keypress.
14480      */
14481     forceKeyDown : false,
14482
14483     // private
14484     prepareEvent : function(e){
14485         var k = e.getKey();
14486         var h = this.keyToHandler[k];
14487         //if(h && this[h]){
14488         //    e.stopPropagation();
14489         //}
14490         if(Roo.isSafari && h && k >= 37 && k <= 40){
14491             e.stopEvent();
14492         }
14493     },
14494
14495     // private
14496     relay : function(e){
14497         var k = e.getKey();
14498         var h = this.keyToHandler[k];
14499         if(h && this[h]){
14500             if(this.doRelay(e, this[h], h) !== true){
14501                 e[this.defaultEventAction]();
14502             }
14503         }
14504     },
14505
14506     // private
14507     doRelay : function(e, h, hname){
14508         return h.call(this.scope || this, e);
14509     },
14510
14511     // possible handlers
14512     enter : false,
14513     left : false,
14514     right : false,
14515     up : false,
14516     down : false,
14517     tab : false,
14518     esc : false,
14519     pageUp : false,
14520     pageDown : false,
14521     del : false,
14522     home : false,
14523     end : false,
14524
14525     // quick lookup hash
14526     keyToHandler : {
14527         37 : "left",
14528         39 : "right",
14529         38 : "up",
14530         40 : "down",
14531         33 : "pageUp",
14532         34 : "pageDown",
14533         46 : "del",
14534         36 : "home",
14535         35 : "end",
14536         13 : "enter",
14537         27 : "esc",
14538         9  : "tab"
14539     },
14540
14541         /**
14542          * Enable this KeyNav
14543          */
14544         enable: function(){
14545                 if(this.disabled){
14546             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14547             // the EventObject will normalize Safari automatically
14548             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14549                 this.el.on("keydown", this.relay,  this);
14550             }else{
14551                 this.el.on("keydown", this.prepareEvent,  this);
14552                 this.el.on("keypress", this.relay,  this);
14553             }
14554                     this.disabled = false;
14555                 }
14556         },
14557
14558         /**
14559          * Disable this KeyNav
14560          */
14561         disable: function(){
14562                 if(!this.disabled){
14563                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14564                 this.el.un("keydown", this.relay);
14565             }else{
14566                 this.el.un("keydown", this.prepareEvent);
14567                 this.el.un("keypress", this.relay);
14568             }
14569                     this.disabled = true;
14570                 }
14571         }
14572 };/*
14573  * Based on:
14574  * Ext JS Library 1.1.1
14575  * Copyright(c) 2006-2007, Ext JS, LLC.
14576  *
14577  * Originally Released Under LGPL - original licence link has changed is not relivant.
14578  *
14579  * Fork - LGPL
14580  * <script type="text/javascript">
14581  */
14582
14583  
14584 /**
14585  * @class Roo.KeyMap
14586  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14587  * The constructor accepts the same config object as defined by {@link #addBinding}.
14588  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14589  * combination it will call the function with this signature (if the match is a multi-key
14590  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14591  * A KeyMap can also handle a string representation of keys.<br />
14592  * Usage:
14593  <pre><code>
14594 // map one key by key code
14595 var map = new Roo.KeyMap("my-element", {
14596     key: 13, // or Roo.EventObject.ENTER
14597     fn: myHandler,
14598     scope: myObject
14599 });
14600
14601 // map multiple keys to one action by string
14602 var map = new Roo.KeyMap("my-element", {
14603     key: "a\r\n\t",
14604     fn: myHandler,
14605     scope: myObject
14606 });
14607
14608 // map multiple keys to multiple actions by strings and array of codes
14609 var map = new Roo.KeyMap("my-element", [
14610     {
14611         key: [10,13],
14612         fn: function(){ alert("Return was pressed"); }
14613     }, {
14614         key: "abc",
14615         fn: function(){ alert('a, b or c was pressed'); }
14616     }, {
14617         key: "\t",
14618         ctrl:true,
14619         shift:true,
14620         fn: function(){ alert('Control + shift + tab was pressed.'); }
14621     }
14622 ]);
14623 </code></pre>
14624  * <b>Note: A KeyMap starts enabled</b>
14625  * @constructor
14626  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14627  * @param {Object} config The config (see {@link #addBinding})
14628  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14629  */
14630 Roo.KeyMap = function(el, config, eventName){
14631     this.el  = Roo.get(el);
14632     this.eventName = eventName || "keydown";
14633     this.bindings = [];
14634     if(config){
14635         this.addBinding(config);
14636     }
14637     this.enable();
14638 };
14639
14640 Roo.KeyMap.prototype = {
14641     /**
14642      * True to stop the event from bubbling and prevent the default browser action if the
14643      * key was handled by the KeyMap (defaults to false)
14644      * @type Boolean
14645      */
14646     stopEvent : false,
14647
14648     /**
14649      * Add a new binding to this KeyMap. The following config object properties are supported:
14650      * <pre>
14651 Property    Type             Description
14652 ----------  ---------------  ----------------------------------------------------------------------
14653 key         String/Array     A single keycode or an array of keycodes to handle
14654 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14655 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14656 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14657 fn          Function         The function to call when KeyMap finds the expected key combination
14658 scope       Object           The scope of the callback function
14659 </pre>
14660      *
14661      * Usage:
14662      * <pre><code>
14663 // Create a KeyMap
14664 var map = new Roo.KeyMap(document, {
14665     key: Roo.EventObject.ENTER,
14666     fn: handleKey,
14667     scope: this
14668 });
14669
14670 //Add a new binding to the existing KeyMap later
14671 map.addBinding({
14672     key: 'abc',
14673     shift: true,
14674     fn: handleKey,
14675     scope: this
14676 });
14677 </code></pre>
14678      * @param {Object/Array} config A single KeyMap config or an array of configs
14679      */
14680         addBinding : function(config){
14681         if(config instanceof Array){
14682             for(var i = 0, len = config.length; i < len; i++){
14683                 this.addBinding(config[i]);
14684             }
14685             return;
14686         }
14687         var keyCode = config.key,
14688             shift = config.shift, 
14689             ctrl = config.ctrl, 
14690             alt = config.alt,
14691             fn = config.fn,
14692             scope = config.scope;
14693         if(typeof keyCode == "string"){
14694             var ks = [];
14695             var keyString = keyCode.toUpperCase();
14696             for(var j = 0, len = keyString.length; j < len; j++){
14697                 ks.push(keyString.charCodeAt(j));
14698             }
14699             keyCode = ks;
14700         }
14701         var keyArray = keyCode instanceof Array;
14702         var handler = function(e){
14703             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14704                 var k = e.getKey();
14705                 if(keyArray){
14706                     for(var i = 0, len = keyCode.length; i < len; i++){
14707                         if(keyCode[i] == k){
14708                           if(this.stopEvent){
14709                               e.stopEvent();
14710                           }
14711                           fn.call(scope || window, k, e);
14712                           return;
14713                         }
14714                     }
14715                 }else{
14716                     if(k == keyCode){
14717                         if(this.stopEvent){
14718                            e.stopEvent();
14719                         }
14720                         fn.call(scope || window, k, e);
14721                     }
14722                 }
14723             }
14724         };
14725         this.bindings.push(handler);  
14726         },
14727
14728     /**
14729      * Shorthand for adding a single key listener
14730      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14731      * following options:
14732      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14733      * @param {Function} fn The function to call
14734      * @param {Object} scope (optional) The scope of the function
14735      */
14736     on : function(key, fn, scope){
14737         var keyCode, shift, ctrl, alt;
14738         if(typeof key == "object" && !(key instanceof Array)){
14739             keyCode = key.key;
14740             shift = key.shift;
14741             ctrl = key.ctrl;
14742             alt = key.alt;
14743         }else{
14744             keyCode = key;
14745         }
14746         this.addBinding({
14747             key: keyCode,
14748             shift: shift,
14749             ctrl: ctrl,
14750             alt: alt,
14751             fn: fn,
14752             scope: scope
14753         })
14754     },
14755
14756     // private
14757     handleKeyDown : function(e){
14758             if(this.enabled){ //just in case
14759             var b = this.bindings;
14760             for(var i = 0, len = b.length; i < len; i++){
14761                 b[i].call(this, e);
14762             }
14763             }
14764         },
14765         
14766         /**
14767          * Returns true if this KeyMap is enabled
14768          * @return {Boolean} 
14769          */
14770         isEnabled : function(){
14771             return this.enabled;  
14772         },
14773         
14774         /**
14775          * Enables this KeyMap
14776          */
14777         enable: function(){
14778                 if(!this.enabled){
14779                     this.el.on(this.eventName, this.handleKeyDown, this);
14780                     this.enabled = true;
14781                 }
14782         },
14783
14784         /**
14785          * Disable this KeyMap
14786          */
14787         disable: function(){
14788                 if(this.enabled){
14789                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14790                     this.enabled = false;
14791                 }
14792         }
14793 };/*
14794  * Based on:
14795  * Ext JS Library 1.1.1
14796  * Copyright(c) 2006-2007, Ext JS, LLC.
14797  *
14798  * Originally Released Under LGPL - original licence link has changed is not relivant.
14799  *
14800  * Fork - LGPL
14801  * <script type="text/javascript">
14802  */
14803
14804  
14805 /**
14806  * @class Roo.util.TextMetrics
14807  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14808  * wide, in pixels, a given block of text will be.
14809  * @singleton
14810  */
14811 Roo.util.TextMetrics = function(){
14812     var shared;
14813     return {
14814         /**
14815          * Measures the size of the specified text
14816          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14817          * that can affect the size of the rendered text
14818          * @param {String} text The text to measure
14819          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14820          * in order to accurately measure the text height
14821          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14822          */
14823         measure : function(el, text, fixedWidth){
14824             if(!shared){
14825                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14826             }
14827             shared.bind(el);
14828             shared.setFixedWidth(fixedWidth || 'auto');
14829             return shared.getSize(text);
14830         },
14831
14832         /**
14833          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14834          * the overhead of multiple calls to initialize the style properties on each measurement.
14835          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14836          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14837          * in order to accurately measure the text height
14838          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14839          */
14840         createInstance : function(el, fixedWidth){
14841             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14842         }
14843     };
14844 }();
14845
14846  
14847
14848 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14849     var ml = new Roo.Element(document.createElement('div'));
14850     document.body.appendChild(ml.dom);
14851     ml.position('absolute');
14852     ml.setLeftTop(-1000, -1000);
14853     ml.hide();
14854
14855     if(fixedWidth){
14856         ml.setWidth(fixedWidth);
14857     }
14858      
14859     var instance = {
14860         /**
14861          * Returns the size of the specified text based on the internal element's style and width properties
14862          * @memberOf Roo.util.TextMetrics.Instance#
14863          * @param {String} text The text to measure
14864          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14865          */
14866         getSize : function(text){
14867             ml.update(text);
14868             var s = ml.getSize();
14869             ml.update('');
14870             return s;
14871         },
14872
14873         /**
14874          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14875          * that can affect the size of the rendered text
14876          * @memberOf Roo.util.TextMetrics.Instance#
14877          * @param {String/HTMLElement} el The element, dom node or id
14878          */
14879         bind : function(el){
14880             ml.setStyle(
14881                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14882             );
14883         },
14884
14885         /**
14886          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14887          * to set a fixed width in order to accurately measure the text height.
14888          * @memberOf Roo.util.TextMetrics.Instance#
14889          * @param {Number} width The width to set on the element
14890          */
14891         setFixedWidth : function(width){
14892             ml.setWidth(width);
14893         },
14894
14895         /**
14896          * Returns the measured width of the specified text
14897          * @memberOf Roo.util.TextMetrics.Instance#
14898          * @param {String} text The text to measure
14899          * @return {Number} width The width in pixels
14900          */
14901         getWidth : function(text){
14902             ml.dom.style.width = 'auto';
14903             return this.getSize(text).width;
14904         },
14905
14906         /**
14907          * Returns the measured height of the specified text.  For multiline text, be sure to call
14908          * {@link #setFixedWidth} if necessary.
14909          * @memberOf Roo.util.TextMetrics.Instance#
14910          * @param {String} text The text to measure
14911          * @return {Number} height The height in pixels
14912          */
14913         getHeight : function(text){
14914             return this.getSize(text).height;
14915         }
14916     };
14917
14918     instance.bind(bindTo);
14919
14920     return instance;
14921 };
14922
14923 // backwards compat
14924 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14925  * Based on:
14926  * Ext JS Library 1.1.1
14927  * Copyright(c) 2006-2007, Ext JS, LLC.
14928  *
14929  * Originally Released Under LGPL - original licence link has changed is not relivant.
14930  *
14931  * Fork - LGPL
14932  * <script type="text/javascript">
14933  */
14934
14935 /**
14936  * @class Roo.state.Provider
14937  * Abstract base class for state provider implementations. This class provides methods
14938  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14939  * Provider interface.
14940  */
14941 Roo.state.Provider = function(){
14942     /**
14943      * @event statechange
14944      * Fires when a state change occurs.
14945      * @param {Provider} this This state provider
14946      * @param {String} key The state key which was changed
14947      * @param {String} value The encoded value for the state
14948      */
14949     this.addEvents({
14950         "statechange": true
14951     });
14952     this.state = {};
14953     Roo.state.Provider.superclass.constructor.call(this);
14954 };
14955 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14956     /**
14957      * Returns the current value for a key
14958      * @param {String} name The key name
14959      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14960      * @return {Mixed} The state data
14961      */
14962     get : function(name, defaultValue){
14963         return typeof this.state[name] == "undefined" ?
14964             defaultValue : this.state[name];
14965     },
14966     
14967     /**
14968      * Clears a value from the state
14969      * @param {String} name The key name
14970      */
14971     clear : function(name){
14972         delete this.state[name];
14973         this.fireEvent("statechange", this, name, null);
14974     },
14975     
14976     /**
14977      * Sets the value for a key
14978      * @param {String} name The key name
14979      * @param {Mixed} value The value to set
14980      */
14981     set : function(name, value){
14982         this.state[name] = value;
14983         this.fireEvent("statechange", this, name, value);
14984     },
14985     
14986     /**
14987      * Decodes a string previously encoded with {@link #encodeValue}.
14988      * @param {String} value The value to decode
14989      * @return {Mixed} The decoded value
14990      */
14991     decodeValue : function(cookie){
14992         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14993         var matches = re.exec(unescape(cookie));
14994         if(!matches || !matches[1]) {
14995             return; // non state cookie
14996         }
14997         var type = matches[1];
14998         var v = matches[2];
14999         switch(type){
15000             case "n":
15001                 return parseFloat(v);
15002             case "d":
15003                 return new Date(Date.parse(v));
15004             case "b":
15005                 return (v == "1");
15006             case "a":
15007                 var all = [];
15008                 var values = v.split("^");
15009                 for(var i = 0, len = values.length; i < len; i++){
15010                     all.push(this.decodeValue(values[i]));
15011                 }
15012                 return all;
15013            case "o":
15014                 var all = {};
15015                 var values = v.split("^");
15016                 for(var i = 0, len = values.length; i < len; i++){
15017                     var kv = values[i].split("=");
15018                     all[kv[0]] = this.decodeValue(kv[1]);
15019                 }
15020                 return all;
15021            default:
15022                 return v;
15023         }
15024     },
15025     
15026     /**
15027      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15028      * @param {Mixed} value The value to encode
15029      * @return {String} The encoded value
15030      */
15031     encodeValue : function(v){
15032         var enc;
15033         if(typeof v == "number"){
15034             enc = "n:" + v;
15035         }else if(typeof v == "boolean"){
15036             enc = "b:" + (v ? "1" : "0");
15037         }else if(v instanceof Date){
15038             enc = "d:" + v.toGMTString();
15039         }else if(v instanceof Array){
15040             var flat = "";
15041             for(var i = 0, len = v.length; i < len; i++){
15042                 flat += this.encodeValue(v[i]);
15043                 if(i != len-1) {
15044                     flat += "^";
15045                 }
15046             }
15047             enc = "a:" + flat;
15048         }else if(typeof v == "object"){
15049             var flat = "";
15050             for(var key in v){
15051                 if(typeof v[key] != "function"){
15052                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15053                 }
15054             }
15055             enc = "o:" + flat.substring(0, flat.length-1);
15056         }else{
15057             enc = "s:" + v;
15058         }
15059         return escape(enc);        
15060     }
15061 });
15062
15063 /*
15064  * Based on:
15065  * Ext JS Library 1.1.1
15066  * Copyright(c) 2006-2007, Ext JS, LLC.
15067  *
15068  * Originally Released Under LGPL - original licence link has changed is not relivant.
15069  *
15070  * Fork - LGPL
15071  * <script type="text/javascript">
15072  */
15073 /**
15074  * @class Roo.state.Manager
15075  * This is the global state manager. By default all components that are "state aware" check this class
15076  * for state information if you don't pass them a custom state provider. In order for this class
15077  * to be useful, it must be initialized with a provider when your application initializes.
15078  <pre><code>
15079 // in your initialization function
15080 init : function(){
15081    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15082    ...
15083    // supposed you have a {@link Roo.BorderLayout}
15084    var layout = new Roo.BorderLayout(...);
15085    layout.restoreState();
15086    // or a {Roo.BasicDialog}
15087    var dialog = new Roo.BasicDialog(...);
15088    dialog.restoreState();
15089  </code></pre>
15090  * @singleton
15091  */
15092 Roo.state.Manager = function(){
15093     var provider = new Roo.state.Provider();
15094     
15095     return {
15096         /**
15097          * Configures the default state provider for your application
15098          * @param {Provider} stateProvider The state provider to set
15099          */
15100         setProvider : function(stateProvider){
15101             provider = stateProvider;
15102         },
15103         
15104         /**
15105          * Returns the current value for a key
15106          * @param {String} name The key name
15107          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15108          * @return {Mixed} The state data
15109          */
15110         get : function(key, defaultValue){
15111             return provider.get(key, defaultValue);
15112         },
15113         
15114         /**
15115          * Sets the value for a key
15116          * @param {String} name The key name
15117          * @param {Mixed} value The state data
15118          */
15119          set : function(key, value){
15120             provider.set(key, value);
15121         },
15122         
15123         /**
15124          * Clears a value from the state
15125          * @param {String} name The key name
15126          */
15127         clear : function(key){
15128             provider.clear(key);
15129         },
15130         
15131         /**
15132          * Gets the currently configured state provider
15133          * @return {Provider} The state provider
15134          */
15135         getProvider : function(){
15136             return provider;
15137         }
15138     };
15139 }();
15140 /*
15141  * Based on:
15142  * Ext JS Library 1.1.1
15143  * Copyright(c) 2006-2007, Ext JS, LLC.
15144  *
15145  * Originally Released Under LGPL - original licence link has changed is not relivant.
15146  *
15147  * Fork - LGPL
15148  * <script type="text/javascript">
15149  */
15150 /**
15151  * @class Roo.state.CookieProvider
15152  * @extends Roo.state.Provider
15153  * The default Provider implementation which saves state via cookies.
15154  * <br />Usage:
15155  <pre><code>
15156    var cp = new Roo.state.CookieProvider({
15157        path: "/cgi-bin/",
15158        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15159        domain: "roojs.com"
15160    })
15161    Roo.state.Manager.setProvider(cp);
15162  </code></pre>
15163  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15164  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15165  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15166  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15167  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15168  * domain the page is running on including the 'www' like 'www.roojs.com')
15169  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15170  * @constructor
15171  * Create a new CookieProvider
15172  * @param {Object} config The configuration object
15173  */
15174 Roo.state.CookieProvider = function(config){
15175     Roo.state.CookieProvider.superclass.constructor.call(this);
15176     this.path = "/";
15177     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15178     this.domain = null;
15179     this.secure = false;
15180     Roo.apply(this, config);
15181     this.state = this.readCookies();
15182 };
15183
15184 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15185     // private
15186     set : function(name, value){
15187         if(typeof value == "undefined" || value === null){
15188             this.clear(name);
15189             return;
15190         }
15191         this.setCookie(name, value);
15192         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15193     },
15194
15195     // private
15196     clear : function(name){
15197         this.clearCookie(name);
15198         Roo.state.CookieProvider.superclass.clear.call(this, name);
15199     },
15200
15201     // private
15202     readCookies : function(){
15203         var cookies = {};
15204         var c = document.cookie + ";";
15205         var re = /\s?(.*?)=(.*?);/g;
15206         var matches;
15207         while((matches = re.exec(c)) != null){
15208             var name = matches[1];
15209             var value = matches[2];
15210             if(name && name.substring(0,3) == "ys-"){
15211                 cookies[name.substr(3)] = this.decodeValue(value);
15212             }
15213         }
15214         return cookies;
15215     },
15216
15217     // private
15218     setCookie : function(name, value){
15219         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15220            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15221            ((this.path == null) ? "" : ("; path=" + this.path)) +
15222            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15223            ((this.secure == true) ? "; secure" : "");
15224     },
15225
15226     // private
15227     clearCookie : function(name){
15228         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15229            ((this.path == null) ? "" : ("; path=" + this.path)) +
15230            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15231            ((this.secure == true) ? "; secure" : "");
15232     }
15233 });/*
15234  * Based on:
15235  * Ext JS Library 1.1.1
15236  * Copyright(c) 2006-2007, Ext JS, LLC.
15237  *
15238  * Originally Released Under LGPL - original licence link has changed is not relivant.
15239  *
15240  * Fork - LGPL
15241  * <script type="text/javascript">
15242  */
15243  
15244
15245 /**
15246  * @class Roo.ComponentMgr
15247  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15248  * @singleton
15249  */
15250 Roo.ComponentMgr = function(){
15251     var all = new Roo.util.MixedCollection();
15252
15253     return {
15254         /**
15255          * Registers a component.
15256          * @param {Roo.Component} c The component
15257          */
15258         register : function(c){
15259             all.add(c);
15260         },
15261
15262         /**
15263          * Unregisters a component.
15264          * @param {Roo.Component} c The component
15265          */
15266         unregister : function(c){
15267             all.remove(c);
15268         },
15269
15270         /**
15271          * Returns a component by id
15272          * @param {String} id The component id
15273          */
15274         get : function(id){
15275             return all.get(id);
15276         },
15277
15278         /**
15279          * Registers a function that will be called when a specified component is added to ComponentMgr
15280          * @param {String} id The component id
15281          * @param {Funtction} fn The callback function
15282          * @param {Object} scope The scope of the callback
15283          */
15284         onAvailable : function(id, fn, scope){
15285             all.on("add", function(index, o){
15286                 if(o.id == id){
15287                     fn.call(scope || o, o);
15288                     all.un("add", fn, scope);
15289                 }
15290             });
15291         }
15292     };
15293 }();/*
15294  * Based on:
15295  * Ext JS Library 1.1.1
15296  * Copyright(c) 2006-2007, Ext JS, LLC.
15297  *
15298  * Originally Released Under LGPL - original licence link has changed is not relivant.
15299  *
15300  * Fork - LGPL
15301  * <script type="text/javascript">
15302  */
15303  
15304 /**
15305  * @class Roo.Component
15306  * @extends Roo.util.Observable
15307  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15308  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15309  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15310  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15311  * All visual components (widgets) that require rendering into a layout should subclass Component.
15312  * @constructor
15313  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15314  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15315  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15316  */
15317 Roo.Component = function(config){
15318     config = config || {};
15319     if(config.tagName || config.dom || typeof config == "string"){ // element object
15320         config = {el: config, id: config.id || config};
15321     }
15322     this.initialConfig = config;
15323
15324     Roo.apply(this, config);
15325     this.addEvents({
15326         /**
15327          * @event disable
15328          * Fires after the component is disabled.
15329              * @param {Roo.Component} this
15330              */
15331         disable : true,
15332         /**
15333          * @event enable
15334          * Fires after the component is enabled.
15335              * @param {Roo.Component} this
15336              */
15337         enable : true,
15338         /**
15339          * @event beforeshow
15340          * Fires before the component is shown.  Return false to stop the show.
15341              * @param {Roo.Component} this
15342              */
15343         beforeshow : true,
15344         /**
15345          * @event show
15346          * Fires after the component is shown.
15347              * @param {Roo.Component} this
15348              */
15349         show : true,
15350         /**
15351          * @event beforehide
15352          * Fires before the component is hidden. Return false to stop the hide.
15353              * @param {Roo.Component} this
15354              */
15355         beforehide : true,
15356         /**
15357          * @event hide
15358          * Fires after the component is hidden.
15359              * @param {Roo.Component} this
15360              */
15361         hide : true,
15362         /**
15363          * @event beforerender
15364          * Fires before the component is rendered. Return false to stop the render.
15365              * @param {Roo.Component} this
15366              */
15367         beforerender : true,
15368         /**
15369          * @event render
15370          * Fires after the component is rendered.
15371              * @param {Roo.Component} this
15372              */
15373         render : true,
15374         /**
15375          * @event beforedestroy
15376          * Fires before the component is destroyed. Return false to stop the destroy.
15377              * @param {Roo.Component} this
15378              */
15379         beforedestroy : true,
15380         /**
15381          * @event destroy
15382          * Fires after the component is destroyed.
15383              * @param {Roo.Component} this
15384              */
15385         destroy : true
15386     });
15387     if(!this.id){
15388         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15389     }
15390     Roo.ComponentMgr.register(this);
15391     Roo.Component.superclass.constructor.call(this);
15392     this.initComponent();
15393     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15394         this.render(this.renderTo);
15395         delete this.renderTo;
15396     }
15397 };
15398
15399 /** @private */
15400 Roo.Component.AUTO_ID = 1000;
15401
15402 Roo.extend(Roo.Component, Roo.util.Observable, {
15403     /**
15404      * @scope Roo.Component.prototype
15405      * @type {Boolean}
15406      * true if this component is hidden. Read-only.
15407      */
15408     hidden : false,
15409     /**
15410      * @type {Boolean}
15411      * true if this component is disabled. Read-only.
15412      */
15413     disabled : false,
15414     /**
15415      * @type {Boolean}
15416      * true if this component has been rendered. Read-only.
15417      */
15418     rendered : false,
15419     
15420     /** @cfg {String} disableClass
15421      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15422      */
15423     disabledClass : "x-item-disabled",
15424         /** @cfg {Boolean} allowDomMove
15425          * Whether the component can move the Dom node when rendering (defaults to true).
15426          */
15427     allowDomMove : true,
15428     /** @cfg {String} hideMode (display|visibility)
15429      * How this component should hidden. Supported values are
15430      * "visibility" (css visibility), "offsets" (negative offset position) and
15431      * "display" (css display) - defaults to "display".
15432      */
15433     hideMode: 'display',
15434
15435     /** @private */
15436     ctype : "Roo.Component",
15437
15438     /**
15439      * @cfg {String} actionMode 
15440      * which property holds the element that used for  hide() / show() / disable() / enable()
15441      * default is 'el' 
15442      */
15443     actionMode : "el",
15444
15445     /** @private */
15446     getActionEl : function(){
15447         return this[this.actionMode];
15448     },
15449
15450     initComponent : Roo.emptyFn,
15451     /**
15452      * If this is a lazy rendering component, render it to its container element.
15453      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15454      */
15455     render : function(container, position){
15456         
15457         if(this.rendered){
15458             return this;
15459         }
15460         
15461         if(this.fireEvent("beforerender", this) === false){
15462             return false;
15463         }
15464         
15465         if(!container && this.el){
15466             this.el = Roo.get(this.el);
15467             container = this.el.dom.parentNode;
15468             this.allowDomMove = false;
15469         }
15470         this.container = Roo.get(container);
15471         this.rendered = true;
15472         if(position !== undefined){
15473             if(typeof position == 'number'){
15474                 position = this.container.dom.childNodes[position];
15475             }else{
15476                 position = Roo.getDom(position);
15477             }
15478         }
15479         this.onRender(this.container, position || null);
15480         if(this.cls){
15481             this.el.addClass(this.cls);
15482             delete this.cls;
15483         }
15484         if(this.style){
15485             this.el.applyStyles(this.style);
15486             delete this.style;
15487         }
15488         this.fireEvent("render", this);
15489         this.afterRender(this.container);
15490         if(this.hidden){
15491             this.hide();
15492         }
15493         if(this.disabled){
15494             this.disable();
15495         }
15496
15497         return this;
15498         
15499     },
15500
15501     /** @private */
15502     // default function is not really useful
15503     onRender : function(ct, position){
15504         if(this.el){
15505             this.el = Roo.get(this.el);
15506             if(this.allowDomMove !== false){
15507                 ct.dom.insertBefore(this.el.dom, position);
15508             }
15509         }
15510     },
15511
15512     /** @private */
15513     getAutoCreate : function(){
15514         var cfg = typeof this.autoCreate == "object" ?
15515                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15516         if(this.id && !cfg.id){
15517             cfg.id = this.id;
15518         }
15519         return cfg;
15520     },
15521
15522     /** @private */
15523     afterRender : Roo.emptyFn,
15524
15525     /**
15526      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15527      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15528      */
15529     destroy : function(){
15530         if(this.fireEvent("beforedestroy", this) !== false){
15531             this.purgeListeners();
15532             this.beforeDestroy();
15533             if(this.rendered){
15534                 this.el.removeAllListeners();
15535                 this.el.remove();
15536                 if(this.actionMode == "container"){
15537                     this.container.remove();
15538                 }
15539             }
15540             this.onDestroy();
15541             Roo.ComponentMgr.unregister(this);
15542             this.fireEvent("destroy", this);
15543         }
15544     },
15545
15546         /** @private */
15547     beforeDestroy : function(){
15548
15549     },
15550
15551         /** @private */
15552         onDestroy : function(){
15553
15554     },
15555
15556     /**
15557      * Returns the underlying {@link Roo.Element}.
15558      * @return {Roo.Element} The element
15559      */
15560     getEl : function(){
15561         return this.el;
15562     },
15563
15564     /**
15565      * Returns the id of this component.
15566      * @return {String}
15567      */
15568     getId : function(){
15569         return this.id;
15570     },
15571
15572     /**
15573      * Try to focus this component.
15574      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15575      * @return {Roo.Component} this
15576      */
15577     focus : function(selectText){
15578         if(this.rendered){
15579             this.el.focus();
15580             if(selectText === true){
15581                 this.el.dom.select();
15582             }
15583         }
15584         return this;
15585     },
15586
15587     /** @private */
15588     blur : function(){
15589         if(this.rendered){
15590             this.el.blur();
15591         }
15592         return this;
15593     },
15594
15595     /**
15596      * Disable this component.
15597      * @return {Roo.Component} this
15598      */
15599     disable : function(){
15600         if(this.rendered){
15601             this.onDisable();
15602         }
15603         this.disabled = true;
15604         this.fireEvent("disable", this);
15605         return this;
15606     },
15607
15608         // private
15609     onDisable : function(){
15610         this.getActionEl().addClass(this.disabledClass);
15611         this.el.dom.disabled = true;
15612     },
15613
15614     /**
15615      * Enable this component.
15616      * @return {Roo.Component} this
15617      */
15618     enable : function(){
15619         if(this.rendered){
15620             this.onEnable();
15621         }
15622         this.disabled = false;
15623         this.fireEvent("enable", this);
15624         return this;
15625     },
15626
15627         // private
15628     onEnable : function(){
15629         this.getActionEl().removeClass(this.disabledClass);
15630         this.el.dom.disabled = false;
15631     },
15632
15633     /**
15634      * Convenience function for setting disabled/enabled by boolean.
15635      * @param {Boolean} disabled
15636      */
15637     setDisabled : function(disabled){
15638         this[disabled ? "disable" : "enable"]();
15639     },
15640
15641     /**
15642      * Show this component.
15643      * @return {Roo.Component} this
15644      */
15645     show: function(){
15646         if(this.fireEvent("beforeshow", this) !== false){
15647             this.hidden = false;
15648             if(this.rendered){
15649                 this.onShow();
15650             }
15651             this.fireEvent("show", this);
15652         }
15653         return this;
15654     },
15655
15656     // private
15657     onShow : function(){
15658         var ae = this.getActionEl();
15659         if(this.hideMode == 'visibility'){
15660             ae.dom.style.visibility = "visible";
15661         }else if(this.hideMode == 'offsets'){
15662             ae.removeClass('x-hidden');
15663         }else{
15664             ae.dom.style.display = "";
15665         }
15666     },
15667
15668     /**
15669      * Hide this component.
15670      * @return {Roo.Component} this
15671      */
15672     hide: function(){
15673         if(this.fireEvent("beforehide", this) !== false){
15674             this.hidden = true;
15675             if(this.rendered){
15676                 this.onHide();
15677             }
15678             this.fireEvent("hide", this);
15679         }
15680         return this;
15681     },
15682
15683     // private
15684     onHide : function(){
15685         var ae = this.getActionEl();
15686         if(this.hideMode == 'visibility'){
15687             ae.dom.style.visibility = "hidden";
15688         }else if(this.hideMode == 'offsets'){
15689             ae.addClass('x-hidden');
15690         }else{
15691             ae.dom.style.display = "none";
15692         }
15693     },
15694
15695     /**
15696      * Convenience function to hide or show this component by boolean.
15697      * @param {Boolean} visible True to show, false to hide
15698      * @return {Roo.Component} this
15699      */
15700     setVisible: function(visible){
15701         if(visible) {
15702             this.show();
15703         }else{
15704             this.hide();
15705         }
15706         return this;
15707     },
15708
15709     /**
15710      * Returns true if this component is visible.
15711      */
15712     isVisible : function(){
15713         return this.getActionEl().isVisible();
15714     },
15715
15716     cloneConfig : function(overrides){
15717         overrides = overrides || {};
15718         var id = overrides.id || Roo.id();
15719         var cfg = Roo.applyIf(overrides, this.initialConfig);
15720         cfg.id = id; // prevent dup id
15721         return new this.constructor(cfg);
15722     }
15723 });/*
15724  * Based on:
15725  * Ext JS Library 1.1.1
15726  * Copyright(c) 2006-2007, Ext JS, LLC.
15727  *
15728  * Originally Released Under LGPL - original licence link has changed is not relivant.
15729  *
15730  * Fork - LGPL
15731  * <script type="text/javascript">
15732  */
15733
15734 /**
15735  * @class Roo.BoxComponent
15736  * @extends Roo.Component
15737  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15738  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15739  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15740  * layout containers.
15741  * @constructor
15742  * @param {Roo.Element/String/Object} config The configuration options.
15743  */
15744 Roo.BoxComponent = function(config){
15745     Roo.Component.call(this, config);
15746     this.addEvents({
15747         /**
15748          * @event resize
15749          * Fires after the component is resized.
15750              * @param {Roo.Component} this
15751              * @param {Number} adjWidth The box-adjusted width that was set
15752              * @param {Number} adjHeight The box-adjusted height that was set
15753              * @param {Number} rawWidth The width that was originally specified
15754              * @param {Number} rawHeight The height that was originally specified
15755              */
15756         resize : true,
15757         /**
15758          * @event move
15759          * Fires after the component is moved.
15760              * @param {Roo.Component} this
15761              * @param {Number} x The new x position
15762              * @param {Number} y The new y position
15763              */
15764         move : true
15765     });
15766 };
15767
15768 Roo.extend(Roo.BoxComponent, Roo.Component, {
15769     // private, set in afterRender to signify that the component has been rendered
15770     boxReady : false,
15771     // private, used to defer height settings to subclasses
15772     deferHeight: false,
15773     /** @cfg {Number} width
15774      * width (optional) size of component
15775      */
15776      /** @cfg {Number} height
15777      * height (optional) size of component
15778      */
15779      
15780     /**
15781      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15782      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15783      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15784      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15785      * @return {Roo.BoxComponent} this
15786      */
15787     setSize : function(w, h){
15788         // support for standard size objects
15789         if(typeof w == 'object'){
15790             h = w.height;
15791             w = w.width;
15792         }
15793         // not rendered
15794         if(!this.boxReady){
15795             this.width = w;
15796             this.height = h;
15797             return this;
15798         }
15799
15800         // prevent recalcs when not needed
15801         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15802             return this;
15803         }
15804         this.lastSize = {width: w, height: h};
15805
15806         var adj = this.adjustSize(w, h);
15807         var aw = adj.width, ah = adj.height;
15808         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15809             var rz = this.getResizeEl();
15810             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15811                 rz.setSize(aw, ah);
15812             }else if(!this.deferHeight && ah !== undefined){
15813                 rz.setHeight(ah);
15814             }else if(aw !== undefined){
15815                 rz.setWidth(aw);
15816             }
15817             this.onResize(aw, ah, w, h);
15818             this.fireEvent('resize', this, aw, ah, w, h);
15819         }
15820         return this;
15821     },
15822
15823     /**
15824      * Gets the current size of the component's underlying element.
15825      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15826      */
15827     getSize : function(){
15828         return this.el.getSize();
15829     },
15830
15831     /**
15832      * Gets the current XY position of the component's underlying element.
15833      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15834      * @return {Array} The XY position of the element (e.g., [100, 200])
15835      */
15836     getPosition : function(local){
15837         if(local === true){
15838             return [this.el.getLeft(true), this.el.getTop(true)];
15839         }
15840         return this.xy || this.el.getXY();
15841     },
15842
15843     /**
15844      * Gets the current box measurements of the component's underlying element.
15845      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15846      * @returns {Object} box An object in the format {x, y, width, height}
15847      */
15848     getBox : function(local){
15849         var s = this.el.getSize();
15850         if(local){
15851             s.x = this.el.getLeft(true);
15852             s.y = this.el.getTop(true);
15853         }else{
15854             var xy = this.xy || this.el.getXY();
15855             s.x = xy[0];
15856             s.y = xy[1];
15857         }
15858         return s;
15859     },
15860
15861     /**
15862      * Sets the current box measurements of the component's underlying element.
15863      * @param {Object} box An object in the format {x, y, width, height}
15864      * @returns {Roo.BoxComponent} this
15865      */
15866     updateBox : function(box){
15867         this.setSize(box.width, box.height);
15868         this.setPagePosition(box.x, box.y);
15869         return this;
15870     },
15871
15872     // protected
15873     getResizeEl : function(){
15874         return this.resizeEl || this.el;
15875     },
15876
15877     // protected
15878     getPositionEl : function(){
15879         return this.positionEl || this.el;
15880     },
15881
15882     /**
15883      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15884      * This method fires the move event.
15885      * @param {Number} left The new left
15886      * @param {Number} top The new top
15887      * @returns {Roo.BoxComponent} this
15888      */
15889     setPosition : function(x, y){
15890         this.x = x;
15891         this.y = y;
15892         if(!this.boxReady){
15893             return this;
15894         }
15895         var adj = this.adjustPosition(x, y);
15896         var ax = adj.x, ay = adj.y;
15897
15898         var el = this.getPositionEl();
15899         if(ax !== undefined || ay !== undefined){
15900             if(ax !== undefined && ay !== undefined){
15901                 el.setLeftTop(ax, ay);
15902             }else if(ax !== undefined){
15903                 el.setLeft(ax);
15904             }else if(ay !== undefined){
15905                 el.setTop(ay);
15906             }
15907             this.onPosition(ax, ay);
15908             this.fireEvent('move', this, ax, ay);
15909         }
15910         return this;
15911     },
15912
15913     /**
15914      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15915      * This method fires the move event.
15916      * @param {Number} x The new x position
15917      * @param {Number} y The new y position
15918      * @returns {Roo.BoxComponent} this
15919      */
15920     setPagePosition : function(x, y){
15921         this.pageX = x;
15922         this.pageY = y;
15923         if(!this.boxReady){
15924             return;
15925         }
15926         if(x === undefined || y === undefined){ // cannot translate undefined points
15927             return;
15928         }
15929         var p = this.el.translatePoints(x, y);
15930         this.setPosition(p.left, p.top);
15931         return this;
15932     },
15933
15934     // private
15935     onRender : function(ct, position){
15936         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15937         if(this.resizeEl){
15938             this.resizeEl = Roo.get(this.resizeEl);
15939         }
15940         if(this.positionEl){
15941             this.positionEl = Roo.get(this.positionEl);
15942         }
15943     },
15944
15945     // private
15946     afterRender : function(){
15947         Roo.BoxComponent.superclass.afterRender.call(this);
15948         this.boxReady = true;
15949         this.setSize(this.width, this.height);
15950         if(this.x || this.y){
15951             this.setPosition(this.x, this.y);
15952         }
15953         if(this.pageX || this.pageY){
15954             this.setPagePosition(this.pageX, this.pageY);
15955         }
15956     },
15957
15958     /**
15959      * Force the component's size to recalculate based on the underlying element's current height and width.
15960      * @returns {Roo.BoxComponent} this
15961      */
15962     syncSize : function(){
15963         delete this.lastSize;
15964         this.setSize(this.el.getWidth(), this.el.getHeight());
15965         return this;
15966     },
15967
15968     /**
15969      * Called after the component is resized, this method is empty by default but can be implemented by any
15970      * subclass that needs to perform custom logic after a resize occurs.
15971      * @param {Number} adjWidth The box-adjusted width that was set
15972      * @param {Number} adjHeight The box-adjusted height that was set
15973      * @param {Number} rawWidth The width that was originally specified
15974      * @param {Number} rawHeight The height that was originally specified
15975      */
15976     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15977
15978     },
15979
15980     /**
15981      * Called after the component is moved, this method is empty by default but can be implemented by any
15982      * subclass that needs to perform custom logic after a move occurs.
15983      * @param {Number} x The new x position
15984      * @param {Number} y The new y position
15985      */
15986     onPosition : function(x, y){
15987
15988     },
15989
15990     // private
15991     adjustSize : function(w, h){
15992         if(this.autoWidth){
15993             w = 'auto';
15994         }
15995         if(this.autoHeight){
15996             h = 'auto';
15997         }
15998         return {width : w, height: h};
15999     },
16000
16001     // private
16002     adjustPosition : function(x, y){
16003         return {x : x, y: y};
16004     }
16005 });/*
16006  * Original code for Roojs - LGPL
16007  * <script type="text/javascript">
16008  */
16009  
16010 /**
16011  * @class Roo.XComponent
16012  * A delayed Element creator...
16013  * Or a way to group chunks of interface together.
16014  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16015  *  used in conjunction with XComponent.build() it will create an instance of each element,
16016  *  then call addxtype() to build the User interface.
16017  * 
16018  * Mypart.xyx = new Roo.XComponent({
16019
16020     parent : 'Mypart.xyz', // empty == document.element.!!
16021     order : '001',
16022     name : 'xxxx'
16023     region : 'xxxx'
16024     disabled : function() {} 
16025      
16026     tree : function() { // return an tree of xtype declared components
16027         var MODULE = this;
16028         return 
16029         {
16030             xtype : 'NestedLayoutPanel',
16031             // technicall
16032         }
16033      ]
16034  *})
16035  *
16036  *
16037  * It can be used to build a big heiracy, with parent etc.
16038  * or you can just use this to render a single compoent to a dom element
16039  * MYPART.render(Roo.Element | String(id) | dom_element )
16040  *
16041  *
16042  * Usage patterns.
16043  *
16044  * Classic Roo
16045  *
16046  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16047  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16048  *
16049  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16050  *
16051  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16052  * - if mulitple topModules exist, the last one is defined as the top module.
16053  *
16054  * Embeded Roo
16055  * 
16056  * When the top level or multiple modules are to embedded into a existing HTML page,
16057  * the parent element can container '#id' of the element where the module will be drawn.
16058  *
16059  * Bootstrap Roo
16060  *
16061  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16062  * it relies more on a include mechanism, where sub modules are included into an outer page.
16063  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16064  * 
16065  * Bootstrap Roo Included elements
16066  *
16067  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16068  * hence confusing the component builder as it thinks there are multiple top level elements. 
16069  *
16070  * String Over-ride & Translations
16071  *
16072  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16073  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16074  * are needed. @see Roo.XComponent.overlayString  
16075  * 
16076  * 
16077  * 
16078  * @extends Roo.util.Observable
16079  * @constructor
16080  * @param cfg {Object} configuration of component
16081  * 
16082  */
16083 Roo.XComponent = function(cfg) {
16084     Roo.apply(this, cfg);
16085     this.addEvents({ 
16086         /**
16087              * @event built
16088              * Fires when this the componnt is built
16089              * @param {Roo.XComponent} c the component
16090              */
16091         'built' : true
16092         
16093     });
16094     this.region = this.region || 'center'; // default..
16095     Roo.XComponent.register(this);
16096     this.modules = false;
16097     this.el = false; // where the layout goes..
16098     
16099     
16100 }
16101 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16102     /**
16103      * @property el
16104      * The created element (with Roo.factory())
16105      * @type {Roo.Layout}
16106      */
16107     el  : false,
16108     
16109     /**
16110      * @property el
16111      * for BC  - use el in new code
16112      * @type {Roo.Layout}
16113      */
16114     panel : false,
16115     
16116     /**
16117      * @property layout
16118      * for BC  - use el in new code
16119      * @type {Roo.Layout}
16120      */
16121     layout : false,
16122     
16123      /**
16124      * @cfg {Function|boolean} disabled
16125      * If this module is disabled by some rule, return true from the funtion
16126      */
16127     disabled : false,
16128     
16129     /**
16130      * @cfg {String} parent 
16131      * Name of parent element which it get xtype added to..
16132      */
16133     parent: false,
16134     
16135     /**
16136      * @cfg {String} order
16137      * Used to set the order in which elements are created (usefull for multiple tabs)
16138      */
16139     
16140     order : false,
16141     /**
16142      * @cfg {String} name
16143      * String to display while loading.
16144      */
16145     name : false,
16146     /**
16147      * @cfg {String} region
16148      * Region to render component to (defaults to center)
16149      */
16150     region : 'center',
16151     
16152     /**
16153      * @cfg {Array} items
16154      * A single item array - the first element is the root of the tree..
16155      * It's done this way to stay compatible with the Xtype system...
16156      */
16157     items : false,
16158     
16159     /**
16160      * @property _tree
16161      * The method that retuns the tree of parts that make up this compoennt 
16162      * @type {function}
16163      */
16164     _tree  : false,
16165     
16166      /**
16167      * render
16168      * render element to dom or tree
16169      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16170      */
16171     
16172     render : function(el)
16173     {
16174         
16175         el = el || false;
16176         var hp = this.parent ? 1 : 0;
16177         Roo.debug &&  Roo.log(this);
16178         
16179         var tree = this._tree ? this._tree() : this.tree();
16180
16181         
16182         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16183             // if parent is a '#.....' string, then let's use that..
16184             var ename = this.parent.substr(1);
16185             this.parent = false;
16186             Roo.debug && Roo.log(ename);
16187             switch (ename) {
16188                 case 'bootstrap-body':
16189                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16190                         // this is the BorderLayout standard?
16191                        this.parent = { el : true };
16192                        break;
16193                     }
16194                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16195                         // need to insert stuff...
16196                         this.parent =  {
16197                              el : new Roo.bootstrap.layout.Border({
16198                                  el : document.body, 
16199                      
16200                                  center: {
16201                                     titlebar: false,
16202                                     autoScroll:false,
16203                                     closeOnTab: true,
16204                                     tabPosition: 'top',
16205                                       //resizeTabs: true,
16206                                     alwaysShowTabs: true,
16207                                     hideTabs: false
16208                                      //minTabWidth: 140
16209                                  }
16210                              })
16211                         
16212                          };
16213                          break;
16214                     }
16215                          
16216                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16217                         this.parent = { el :  new  Roo.bootstrap.Body() };
16218                         Roo.debug && Roo.log("setting el to doc body");
16219                          
16220                     } else {
16221                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16222                     }
16223                     break;
16224                 case 'bootstrap':
16225                     this.parent = { el : true};
16226                     // fall through
16227                 default:
16228                     el = Roo.get(ename);
16229                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16230                         this.parent = { el : true};
16231                     }
16232                     
16233                     break;
16234             }
16235                 
16236             
16237             if (!el && !this.parent) {
16238                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16239                 return;
16240             }
16241         }
16242         
16243         Roo.debug && Roo.log("EL:");
16244         Roo.debug && Roo.log(el);
16245         Roo.debug && Roo.log("this.parent.el:");
16246         Roo.debug && Roo.log(this.parent.el);
16247         
16248
16249         // altertive root elements ??? - we need a better way to indicate these.
16250         var is_alt = Roo.XComponent.is_alt ||
16251                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16252                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16253                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16254         
16255         
16256         
16257         if (!this.parent && is_alt) {
16258             //el = Roo.get(document.body);
16259             this.parent = { el : true };
16260         }
16261             
16262             
16263         
16264         if (!this.parent) {
16265             
16266             Roo.debug && Roo.log("no parent - creating one");
16267             
16268             el = el ? Roo.get(el) : false;      
16269             
16270             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16271                 
16272                 this.parent =  {
16273                     el : new Roo.bootstrap.layout.Border({
16274                         el: el || document.body,
16275                     
16276                         center: {
16277                             titlebar: false,
16278                             autoScroll:false,
16279                             closeOnTab: true,
16280                             tabPosition: 'top',
16281                              //resizeTabs: true,
16282                             alwaysShowTabs: false,
16283                             hideTabs: true,
16284                             minTabWidth: 140,
16285                             overflow: 'visible'
16286                          }
16287                      })
16288                 };
16289             } else {
16290             
16291                 // it's a top level one..
16292                 this.parent =  {
16293                     el : new Roo.BorderLayout(el || document.body, {
16294                         center: {
16295                             titlebar: false,
16296                             autoScroll:false,
16297                             closeOnTab: true,
16298                             tabPosition: 'top',
16299                              //resizeTabs: true,
16300                             alwaysShowTabs: el && hp? false :  true,
16301                             hideTabs: el || !hp ? true :  false,
16302                             minTabWidth: 140
16303                          }
16304                     })
16305                 };
16306             }
16307         }
16308         
16309         if (!this.parent.el) {
16310                 // probably an old style ctor, which has been disabled.
16311                 return;
16312
16313         }
16314                 // The 'tree' method is  '_tree now' 
16315             
16316         tree.region = tree.region || this.region;
16317         var is_body = false;
16318         if (this.parent.el === true) {
16319             // bootstrap... - body..
16320             if (el) {
16321                 tree.el = el;
16322             }
16323             this.parent.el = Roo.factory(tree);
16324             is_body = true;
16325         }
16326         
16327         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16328         this.fireEvent('built', this);
16329         
16330         this.panel = this.el;
16331         this.layout = this.panel.layout;
16332         this.parentLayout = this.parent.layout  || false;  
16333          
16334     }
16335     
16336 });
16337
16338 Roo.apply(Roo.XComponent, {
16339     /**
16340      * @property  hideProgress
16341      * true to disable the building progress bar.. usefull on single page renders.
16342      * @type Boolean
16343      */
16344     hideProgress : false,
16345     /**
16346      * @property  buildCompleted
16347      * True when the builder has completed building the interface.
16348      * @type Boolean
16349      */
16350     buildCompleted : false,
16351      
16352     /**
16353      * @property  topModule
16354      * the upper most module - uses document.element as it's constructor.
16355      * @type Object
16356      */
16357      
16358     topModule  : false,
16359       
16360     /**
16361      * @property  modules
16362      * array of modules to be created by registration system.
16363      * @type {Array} of Roo.XComponent
16364      */
16365     
16366     modules : [],
16367     /**
16368      * @property  elmodules
16369      * array of modules to be created by which use #ID 
16370      * @type {Array} of Roo.XComponent
16371      */
16372      
16373     elmodules : [],
16374
16375      /**
16376      * @property  is_alt
16377      * Is an alternative Root - normally used by bootstrap or other systems,
16378      *    where the top element in the tree can wrap 'body' 
16379      * @type {boolean}  (default false)
16380      */
16381      
16382     is_alt : false,
16383     /**
16384      * @property  build_from_html
16385      * Build elements from html - used by bootstrap HTML stuff 
16386      *    - this is cleared after build is completed
16387      * @type {boolean}    (default false)
16388      */
16389      
16390     build_from_html : false,
16391     /**
16392      * Register components to be built later.
16393      *
16394      * This solves the following issues
16395      * - Building is not done on page load, but after an authentication process has occured.
16396      * - Interface elements are registered on page load
16397      * - Parent Interface elements may not be loaded before child, so this handles that..
16398      * 
16399      *
16400      * example:
16401      * 
16402      * MyApp.register({
16403           order : '000001',
16404           module : 'Pman.Tab.projectMgr',
16405           region : 'center',
16406           parent : 'Pman.layout',
16407           disabled : false,  // or use a function..
16408         })
16409      
16410      * * @param {Object} details about module
16411      */
16412     register : function(obj) {
16413                 
16414         Roo.XComponent.event.fireEvent('register', obj);
16415         switch(typeof(obj.disabled) ) {
16416                 
16417             case 'undefined':
16418                 break;
16419             
16420             case 'function':
16421                 if ( obj.disabled() ) {
16422                         return;
16423                 }
16424                 break;
16425             
16426             default:
16427                 if (obj.disabled || obj.region == '#disabled') {
16428                         return;
16429                 }
16430                 break;
16431         }
16432                 
16433         this.modules.push(obj);
16434          
16435     },
16436     /**
16437      * convert a string to an object..
16438      * eg. 'AAA.BBB' -> finds AAA.BBB
16439
16440      */
16441     
16442     toObject : function(str)
16443     {
16444         if (!str || typeof(str) == 'object') {
16445             return str;
16446         }
16447         if (str.substring(0,1) == '#') {
16448             return str;
16449         }
16450
16451         var ar = str.split('.');
16452         var rt, o;
16453         rt = ar.shift();
16454             /** eval:var:o */
16455         try {
16456             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16457         } catch (e) {
16458             throw "Module not found : " + str;
16459         }
16460         
16461         if (o === false) {
16462             throw "Module not found : " + str;
16463         }
16464         Roo.each(ar, function(e) {
16465             if (typeof(o[e]) == 'undefined') {
16466                 throw "Module not found : " + str;
16467             }
16468             o = o[e];
16469         });
16470         
16471         return o;
16472         
16473     },
16474     
16475     
16476     /**
16477      * move modules into their correct place in the tree..
16478      * 
16479      */
16480     preBuild : function ()
16481     {
16482         var _t = this;
16483         Roo.each(this.modules , function (obj)
16484         {
16485             Roo.XComponent.event.fireEvent('beforebuild', obj);
16486             
16487             var opar = obj.parent;
16488             try { 
16489                 obj.parent = this.toObject(opar);
16490             } catch(e) {
16491                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
16492                 return;
16493             }
16494             
16495             if (!obj.parent) {
16496                 Roo.debug && Roo.log("GOT top level module");
16497                 Roo.debug && Roo.log(obj);
16498                 obj.modules = new Roo.util.MixedCollection(false, 
16499                     function(o) { return o.order + '' }
16500                 );
16501                 this.topModule = obj;
16502                 return;
16503             }
16504                         // parent is a string (usually a dom element name..)
16505             if (typeof(obj.parent) == 'string') {
16506                 this.elmodules.push(obj);
16507                 return;
16508             }
16509             if (obj.parent.constructor != Roo.XComponent) {
16510                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16511             }
16512             if (!obj.parent.modules) {
16513                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16514                     function(o) { return o.order + '' }
16515                 );
16516             }
16517             if (obj.parent.disabled) {
16518                 obj.disabled = true;
16519             }
16520             obj.parent.modules.add(obj);
16521         }, this);
16522     },
16523     
16524      /**
16525      * make a list of modules to build.
16526      * @return {Array} list of modules. 
16527      */ 
16528     
16529     buildOrder : function()
16530     {
16531         var _this = this;
16532         var cmp = function(a,b) {   
16533             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16534         };
16535         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16536             throw "No top level modules to build";
16537         }
16538         
16539         // make a flat list in order of modules to build.
16540         var mods = this.topModule ? [ this.topModule ] : [];
16541                 
16542         
16543         // elmodules (is a list of DOM based modules )
16544         Roo.each(this.elmodules, function(e) {
16545             mods.push(e);
16546             if (!this.topModule &&
16547                 typeof(e.parent) == 'string' &&
16548                 e.parent.substring(0,1) == '#' &&
16549                 Roo.get(e.parent.substr(1))
16550                ) {
16551                 
16552                 _this.topModule = e;
16553             }
16554             
16555         });
16556
16557         
16558         // add modules to their parents..
16559         var addMod = function(m) {
16560             Roo.debug && Roo.log("build Order: add: " + m.name);
16561                 
16562             mods.push(m);
16563             if (m.modules && !m.disabled) {
16564                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16565                 m.modules.keySort('ASC',  cmp );
16566                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16567     
16568                 m.modules.each(addMod);
16569             } else {
16570                 Roo.debug && Roo.log("build Order: no child modules");
16571             }
16572             // not sure if this is used any more..
16573             if (m.finalize) {
16574                 m.finalize.name = m.name + " (clean up) ";
16575                 mods.push(m.finalize);
16576             }
16577             
16578         }
16579         if (this.topModule && this.topModule.modules) { 
16580             this.topModule.modules.keySort('ASC',  cmp );
16581             this.topModule.modules.each(addMod);
16582         } 
16583         return mods;
16584     },
16585     
16586      /**
16587      * Build the registered modules.
16588      * @param {Object} parent element.
16589      * @param {Function} optional method to call after module has been added.
16590      * 
16591      */ 
16592    
16593     build : function(opts) 
16594     {
16595         
16596         if (typeof(opts) != 'undefined') {
16597             Roo.apply(this,opts);
16598         }
16599         
16600         this.preBuild();
16601         var mods = this.buildOrder();
16602       
16603         //this.allmods = mods;
16604         //Roo.debug && Roo.log(mods);
16605         //return;
16606         if (!mods.length) { // should not happen
16607             throw "NO modules!!!";
16608         }
16609         
16610         
16611         var msg = "Building Interface...";
16612         // flash it up as modal - so we store the mask!?
16613         if (!this.hideProgress && Roo.MessageBox) {
16614             Roo.MessageBox.show({ title: 'loading' });
16615             Roo.MessageBox.show({
16616                title: "Please wait...",
16617                msg: msg,
16618                width:450,
16619                progress:true,
16620                buttons : false,
16621                closable:false,
16622                modal: false
16623               
16624             });
16625         }
16626         var total = mods.length;
16627         
16628         var _this = this;
16629         var progressRun = function() {
16630             if (!mods.length) {
16631                 Roo.debug && Roo.log('hide?');
16632                 if (!this.hideProgress && Roo.MessageBox) {
16633                     Roo.MessageBox.hide();
16634                 }
16635                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16636                 
16637                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16638                 
16639                 // THE END...
16640                 return false;   
16641             }
16642             
16643             var m = mods.shift();
16644             
16645             
16646             Roo.debug && Roo.log(m);
16647             // not sure if this is supported any more.. - modules that are are just function
16648             if (typeof(m) == 'function') { 
16649                 m.call(this);
16650                 return progressRun.defer(10, _this);
16651             } 
16652             
16653             
16654             msg = "Building Interface " + (total  - mods.length) + 
16655                     " of " + total + 
16656                     (m.name ? (' - ' + m.name) : '');
16657                         Roo.debug && Roo.log(msg);
16658             if (!_this.hideProgress &&  Roo.MessageBox) { 
16659                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16660             }
16661             
16662          
16663             // is the module disabled?
16664             var disabled = (typeof(m.disabled) == 'function') ?
16665                 m.disabled.call(m.module.disabled) : m.disabled;    
16666             
16667             
16668             if (disabled) {
16669                 return progressRun(); // we do not update the display!
16670             }
16671             
16672             // now build 
16673             
16674                         
16675                         
16676             m.render();
16677             // it's 10 on top level, and 1 on others??? why...
16678             return progressRun.defer(10, _this);
16679              
16680         }
16681         progressRun.defer(1, _this);
16682      
16683         
16684         
16685     },
16686     /**
16687      * Overlay a set of modified strings onto a component
16688      * This is dependant on our builder exporting the strings and 'named strings' elements.
16689      * 
16690      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
16691      * @param {Object} associative array of 'named' string and it's new value.
16692      * 
16693      */
16694         overlayStrings : function( component, strings )
16695     {
16696         if (typeof(component['_named_strings']) == 'undefined') {
16697             throw "ERROR: component does not have _named_strings";
16698         }
16699         for ( var k in strings ) {
16700             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
16701             if (md !== false) {
16702                 component['_strings'][md] = strings[k];
16703             } else {
16704                 Roo.log('could not find named string: ' + k + ' in');
16705                 Roo.log(component);
16706             }
16707             
16708         }
16709         
16710     },
16711     
16712         
16713         /**
16714          * Event Object.
16715          *
16716          *
16717          */
16718         event: false, 
16719     /**
16720          * wrapper for event.on - aliased later..  
16721          * Typically use to register a event handler for register:
16722          *
16723          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16724          *
16725          */
16726     on : false
16727    
16728     
16729     
16730 });
16731
16732 Roo.XComponent.event = new Roo.util.Observable({
16733                 events : { 
16734                         /**
16735                          * @event register
16736                          * Fires when an Component is registered,
16737                          * set the disable property on the Component to stop registration.
16738                          * @param {Roo.XComponent} c the component being registerd.
16739                          * 
16740                          */
16741                         'register' : true,
16742             /**
16743                          * @event beforebuild
16744                          * Fires before each Component is built
16745                          * can be used to apply permissions.
16746                          * @param {Roo.XComponent} c the component being registerd.
16747                          * 
16748                          */
16749                         'beforebuild' : true,
16750                         /**
16751                          * @event buildcomplete
16752                          * Fires on the top level element when all elements have been built
16753                          * @param {Roo.XComponent} the top level component.
16754                          */
16755                         'buildcomplete' : true
16756                         
16757                 }
16758 });
16759
16760 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16761  //
16762  /**
16763  * marked - a markdown parser
16764  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
16765  * https://github.com/chjj/marked
16766  */
16767
16768
16769 /**
16770  *
16771  * Roo.Markdown - is a very crude wrapper around marked..
16772  *
16773  * usage:
16774  * 
16775  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
16776  * 
16777  * Note: move the sample code to the bottom of this
16778  * file before uncommenting it.
16779  *
16780  */
16781
16782 Roo.Markdown = {};
16783 Roo.Markdown.toHtml = function(text) {
16784     
16785     var c = new Roo.Markdown.marked.setOptions({
16786             renderer: new Roo.Markdown.marked.Renderer(),
16787             gfm: true,
16788             tables: true,
16789             breaks: false,
16790             pedantic: false,
16791             sanitize: false,
16792             smartLists: true,
16793             smartypants: false
16794           });
16795     // A FEW HACKS!!?
16796     
16797     text = text.replace(/\\\n/g,' ');
16798     return Roo.Markdown.marked(text);
16799 };
16800 //
16801 // converter
16802 //
16803 // Wraps all "globals" so that the only thing
16804 // exposed is makeHtml().
16805 //
16806 (function() {
16807     
16808     
16809       
16810     /**
16811      * Helpers
16812      */
16813     
16814     var escape = function (html, encode) {
16815       return html
16816         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
16817         .replace(/</g, '&lt;')
16818         .replace(/>/g, '&gt;')
16819         .replace(/"/g, '&quot;')
16820         .replace(/'/g, '&#39;');
16821     }
16822     
16823     var unescape = function (html) {
16824         // explicitly match decimal, hex, and named HTML entities 
16825       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
16826         n = n.toLowerCase();
16827         if (n === 'colon') { return ':'; }
16828         if (n.charAt(0) === '#') {
16829           return n.charAt(1) === 'x'
16830             ? String.fromCharCode(parseInt(n.substring(2), 16))
16831             : String.fromCharCode(+n.substring(1));
16832         }
16833         return '';
16834       });
16835     }
16836     
16837     var replace = function (regex, opt) {
16838       regex = regex.source;
16839       opt = opt || '';
16840       return function self(name, val) {
16841         if (!name) { return new RegExp(regex, opt); }
16842         val = val.source || val;
16843         val = val.replace(/(^|[^\[])\^/g, '$1');
16844         regex = regex.replace(name, val);
16845         return self;
16846       };
16847     }
16848     
16849     /**
16850      * Block-Level Grammar
16851      */
16852     
16853     
16854     
16855     
16856     var block = {
16857       newline: /^\n+/,
16858       code: /^( {4}[^\n]+\n*)+/,
16859       fences: noop,
16860       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
16861       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
16862       nptable: noop,
16863       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
16864       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
16865       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
16866       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
16867       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
16868       table: noop,
16869       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
16870       text: /^[^\n]+/
16871     };
16872     
16873     block.bullet = /(?:[*+-]|\d+\.)/;
16874     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
16875     block.item = replace(block.item, 'gm')
16876       (/bull/g, block.bullet)
16877       ();
16878     
16879     block.list = replace(block.list)
16880       (/bull/g, block.bullet)
16881       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
16882       ('def', '\\n+(?=' + block.def.source + ')')
16883       ();
16884     
16885     block.blockquote = replace(block.blockquote)
16886       ('def', block.def)
16887       ();
16888     
16889     block._tag = '(?!(?:'
16890       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
16891       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
16892       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
16893     
16894     block.html = replace(block.html)
16895       ('comment', /<!--[\s\S]*?-->/)
16896       ('closed', /<(tag)[\s\S]+?<\/\1>/)
16897       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
16898       (/tag/g, block._tag)
16899       ();
16900     
16901     block.paragraph = replace(block.paragraph)
16902       ('hr', block.hr)
16903       ('heading', block.heading)
16904       ('lheading', block.lheading)
16905       ('blockquote', block.blockquote)
16906       ('tag', '<' + block._tag)
16907       ('def', block.def)
16908       ();
16909     
16910     /**
16911      * Normal Block Grammar
16912      */
16913     
16914     block.normal = merge({}, block);
16915     
16916     /**
16917      * GFM Block Grammar
16918      */
16919     
16920     block.gfm = merge({}, block.normal, {
16921       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
16922       paragraph: /^/,
16923       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
16924     });
16925     
16926     block.gfm.paragraph = replace(block.paragraph)
16927       ('(?!', '(?!'
16928         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
16929         + block.list.source.replace('\\1', '\\3') + '|')
16930       ();
16931     
16932     /**
16933      * GFM + Tables Block Grammar
16934      */
16935     
16936     block.tables = merge({}, block.gfm, {
16937       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
16938       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
16939     });
16940     
16941     /**
16942      * Block Lexer
16943      */
16944     
16945     var Lexer = function (options) {
16946       this.tokens = [];
16947       this.tokens.links = {};
16948       this.options = options || marked.defaults;
16949       this.rules = block.normal;
16950     
16951       if (this.options.gfm) {
16952         if (this.options.tables) {
16953           this.rules = block.tables;
16954         } else {
16955           this.rules = block.gfm;
16956         }
16957       }
16958     }
16959     
16960     /**
16961      * Expose Block Rules
16962      */
16963     
16964     Lexer.rules = block;
16965     
16966     /**
16967      * Static Lex Method
16968      */
16969     
16970     Lexer.lex = function(src, options) {
16971       var lexer = new Lexer(options);
16972       return lexer.lex(src);
16973     };
16974     
16975     /**
16976      * Preprocessing
16977      */
16978     
16979     Lexer.prototype.lex = function(src) {
16980       src = src
16981         .replace(/\r\n|\r/g, '\n')
16982         .replace(/\t/g, '    ')
16983         .replace(/\u00a0/g, ' ')
16984         .replace(/\u2424/g, '\n');
16985     
16986       return this.token(src, true);
16987     };
16988     
16989     /**
16990      * Lexing
16991      */
16992     
16993     Lexer.prototype.token = function(src, top, bq) {
16994       var src = src.replace(/^ +$/gm, '')
16995         , next
16996         , loose
16997         , cap
16998         , bull
16999         , b
17000         , item
17001         , space
17002         , i
17003         , l;
17004     
17005       while (src) {
17006         // newline
17007         if (cap = this.rules.newline.exec(src)) {
17008           src = src.substring(cap[0].length);
17009           if (cap[0].length > 1) {
17010             this.tokens.push({
17011               type: 'space'
17012             });
17013           }
17014         }
17015     
17016         // code
17017         if (cap = this.rules.code.exec(src)) {
17018           src = src.substring(cap[0].length);
17019           cap = cap[0].replace(/^ {4}/gm, '');
17020           this.tokens.push({
17021             type: 'code',
17022             text: !this.options.pedantic
17023               ? cap.replace(/\n+$/, '')
17024               : cap
17025           });
17026           continue;
17027         }
17028     
17029         // fences (gfm)
17030         if (cap = this.rules.fences.exec(src)) {
17031           src = src.substring(cap[0].length);
17032           this.tokens.push({
17033             type: 'code',
17034             lang: cap[2],
17035             text: cap[3] || ''
17036           });
17037           continue;
17038         }
17039     
17040         // heading
17041         if (cap = this.rules.heading.exec(src)) {
17042           src = src.substring(cap[0].length);
17043           this.tokens.push({
17044             type: 'heading',
17045             depth: cap[1].length,
17046             text: cap[2]
17047           });
17048           continue;
17049         }
17050     
17051         // table no leading pipe (gfm)
17052         if (top && (cap = this.rules.nptable.exec(src))) {
17053           src = src.substring(cap[0].length);
17054     
17055           item = {
17056             type: 'table',
17057             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17058             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17059             cells: cap[3].replace(/\n$/, '').split('\n')
17060           };
17061     
17062           for (i = 0; i < item.align.length; i++) {
17063             if (/^ *-+: *$/.test(item.align[i])) {
17064               item.align[i] = 'right';
17065             } else if (/^ *:-+: *$/.test(item.align[i])) {
17066               item.align[i] = 'center';
17067             } else if (/^ *:-+ *$/.test(item.align[i])) {
17068               item.align[i] = 'left';
17069             } else {
17070               item.align[i] = null;
17071             }
17072           }
17073     
17074           for (i = 0; i < item.cells.length; i++) {
17075             item.cells[i] = item.cells[i].split(/ *\| */);
17076           }
17077     
17078           this.tokens.push(item);
17079     
17080           continue;
17081         }
17082     
17083         // lheading
17084         if (cap = this.rules.lheading.exec(src)) {
17085           src = src.substring(cap[0].length);
17086           this.tokens.push({
17087             type: 'heading',
17088             depth: cap[2] === '=' ? 1 : 2,
17089             text: cap[1]
17090           });
17091           continue;
17092         }
17093     
17094         // hr
17095         if (cap = this.rules.hr.exec(src)) {
17096           src = src.substring(cap[0].length);
17097           this.tokens.push({
17098             type: 'hr'
17099           });
17100           continue;
17101         }
17102     
17103         // blockquote
17104         if (cap = this.rules.blockquote.exec(src)) {
17105           src = src.substring(cap[0].length);
17106     
17107           this.tokens.push({
17108             type: 'blockquote_start'
17109           });
17110     
17111           cap = cap[0].replace(/^ *> ?/gm, '');
17112     
17113           // Pass `top` to keep the current
17114           // "toplevel" state. This is exactly
17115           // how markdown.pl works.
17116           this.token(cap, top, true);
17117     
17118           this.tokens.push({
17119             type: 'blockquote_end'
17120           });
17121     
17122           continue;
17123         }
17124     
17125         // list
17126         if (cap = this.rules.list.exec(src)) {
17127           src = src.substring(cap[0].length);
17128           bull = cap[2];
17129     
17130           this.tokens.push({
17131             type: 'list_start',
17132             ordered: bull.length > 1
17133           });
17134     
17135           // Get each top-level item.
17136           cap = cap[0].match(this.rules.item);
17137     
17138           next = false;
17139           l = cap.length;
17140           i = 0;
17141     
17142           for (; i < l; i++) {
17143             item = cap[i];
17144     
17145             // Remove the list item's bullet
17146             // so it is seen as the next token.
17147             space = item.length;
17148             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17149     
17150             // Outdent whatever the
17151             // list item contains. Hacky.
17152             if (~item.indexOf('\n ')) {
17153               space -= item.length;
17154               item = !this.options.pedantic
17155                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17156                 : item.replace(/^ {1,4}/gm, '');
17157             }
17158     
17159             // Determine whether the next list item belongs here.
17160             // Backpedal if it does not belong in this list.
17161             if (this.options.smartLists && i !== l - 1) {
17162               b = block.bullet.exec(cap[i + 1])[0];
17163               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17164                 src = cap.slice(i + 1).join('\n') + src;
17165                 i = l - 1;
17166               }
17167             }
17168     
17169             // Determine whether item is loose or not.
17170             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17171             // for discount behavior.
17172             loose = next || /\n\n(?!\s*$)/.test(item);
17173             if (i !== l - 1) {
17174               next = item.charAt(item.length - 1) === '\n';
17175               if (!loose) { loose = next; }
17176             }
17177     
17178             this.tokens.push({
17179               type: loose
17180                 ? 'loose_item_start'
17181                 : 'list_item_start'
17182             });
17183     
17184             // Recurse.
17185             this.token(item, false, bq);
17186     
17187             this.tokens.push({
17188               type: 'list_item_end'
17189             });
17190           }
17191     
17192           this.tokens.push({
17193             type: 'list_end'
17194           });
17195     
17196           continue;
17197         }
17198     
17199         // html
17200         if (cap = this.rules.html.exec(src)) {
17201           src = src.substring(cap[0].length);
17202           this.tokens.push({
17203             type: this.options.sanitize
17204               ? 'paragraph'
17205               : 'html',
17206             pre: !this.options.sanitizer
17207               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17208             text: cap[0]
17209           });
17210           continue;
17211         }
17212     
17213         // def
17214         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17215           src = src.substring(cap[0].length);
17216           this.tokens.links[cap[1].toLowerCase()] = {
17217             href: cap[2],
17218             title: cap[3]
17219           };
17220           continue;
17221         }
17222     
17223         // table (gfm)
17224         if (top && (cap = this.rules.table.exec(src))) {
17225           src = src.substring(cap[0].length);
17226     
17227           item = {
17228             type: 'table',
17229             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17230             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17231             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17232           };
17233     
17234           for (i = 0; i < item.align.length; i++) {
17235             if (/^ *-+: *$/.test(item.align[i])) {
17236               item.align[i] = 'right';
17237             } else if (/^ *:-+: *$/.test(item.align[i])) {
17238               item.align[i] = 'center';
17239             } else if (/^ *:-+ *$/.test(item.align[i])) {
17240               item.align[i] = 'left';
17241             } else {
17242               item.align[i] = null;
17243             }
17244           }
17245     
17246           for (i = 0; i < item.cells.length; i++) {
17247             item.cells[i] = item.cells[i]
17248               .replace(/^ *\| *| *\| *$/g, '')
17249               .split(/ *\| */);
17250           }
17251     
17252           this.tokens.push(item);
17253     
17254           continue;
17255         }
17256     
17257         // top-level paragraph
17258         if (top && (cap = this.rules.paragraph.exec(src))) {
17259           src = src.substring(cap[0].length);
17260           this.tokens.push({
17261             type: 'paragraph',
17262             text: cap[1].charAt(cap[1].length - 1) === '\n'
17263               ? cap[1].slice(0, -1)
17264               : cap[1]
17265           });
17266           continue;
17267         }
17268     
17269         // text
17270         if (cap = this.rules.text.exec(src)) {
17271           // Top-level should never reach here.
17272           src = src.substring(cap[0].length);
17273           this.tokens.push({
17274             type: 'text',
17275             text: cap[0]
17276           });
17277           continue;
17278         }
17279     
17280         if (src) {
17281           throw new
17282             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17283         }
17284       }
17285     
17286       return this.tokens;
17287     };
17288     
17289     /**
17290      * Inline-Level Grammar
17291      */
17292     
17293     var inline = {
17294       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17295       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17296       url: noop,
17297       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17298       link: /^!?\[(inside)\]\(href\)/,
17299       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17300       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17301       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17302       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17303       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17304       br: /^ {2,}\n(?!\s*$)/,
17305       del: noop,
17306       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17307     };
17308     
17309     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17310     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17311     
17312     inline.link = replace(inline.link)
17313       ('inside', inline._inside)
17314       ('href', inline._href)
17315       ();
17316     
17317     inline.reflink = replace(inline.reflink)
17318       ('inside', inline._inside)
17319       ();
17320     
17321     /**
17322      * Normal Inline Grammar
17323      */
17324     
17325     inline.normal = merge({}, inline);
17326     
17327     /**
17328      * Pedantic Inline Grammar
17329      */
17330     
17331     inline.pedantic = merge({}, inline.normal, {
17332       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17333       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17334     });
17335     
17336     /**
17337      * GFM Inline Grammar
17338      */
17339     
17340     inline.gfm = merge({}, inline.normal, {
17341       escape: replace(inline.escape)('])', '~|])')(),
17342       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17343       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17344       text: replace(inline.text)
17345         (']|', '~]|')
17346         ('|', '|https?://|')
17347         ()
17348     });
17349     
17350     /**
17351      * GFM + Line Breaks Inline Grammar
17352      */
17353     
17354     inline.breaks = merge({}, inline.gfm, {
17355       br: replace(inline.br)('{2,}', '*')(),
17356       text: replace(inline.gfm.text)('{2,}', '*')()
17357     });
17358     
17359     /**
17360      * Inline Lexer & Compiler
17361      */
17362     
17363     var InlineLexer  = function (links, options) {
17364       this.options = options || marked.defaults;
17365       this.links = links;
17366       this.rules = inline.normal;
17367       this.renderer = this.options.renderer || new Renderer;
17368       this.renderer.options = this.options;
17369     
17370       if (!this.links) {
17371         throw new
17372           Error('Tokens array requires a `links` property.');
17373       }
17374     
17375       if (this.options.gfm) {
17376         if (this.options.breaks) {
17377           this.rules = inline.breaks;
17378         } else {
17379           this.rules = inline.gfm;
17380         }
17381       } else if (this.options.pedantic) {
17382         this.rules = inline.pedantic;
17383       }
17384     }
17385     
17386     /**
17387      * Expose Inline Rules
17388      */
17389     
17390     InlineLexer.rules = inline;
17391     
17392     /**
17393      * Static Lexing/Compiling Method
17394      */
17395     
17396     InlineLexer.output = function(src, links, options) {
17397       var inline = new InlineLexer(links, options);
17398       return inline.output(src);
17399     };
17400     
17401     /**
17402      * Lexing/Compiling
17403      */
17404     
17405     InlineLexer.prototype.output = function(src) {
17406       var out = ''
17407         , link
17408         , text
17409         , href
17410         , cap;
17411     
17412       while (src) {
17413         // escape
17414         if (cap = this.rules.escape.exec(src)) {
17415           src = src.substring(cap[0].length);
17416           out += cap[1];
17417           continue;
17418         }
17419     
17420         // autolink
17421         if (cap = this.rules.autolink.exec(src)) {
17422           src = src.substring(cap[0].length);
17423           if (cap[2] === '@') {
17424             text = cap[1].charAt(6) === ':'
17425               ? this.mangle(cap[1].substring(7))
17426               : this.mangle(cap[1]);
17427             href = this.mangle('mailto:') + text;
17428           } else {
17429             text = escape(cap[1]);
17430             href = text;
17431           }
17432           out += this.renderer.link(href, null, text);
17433           continue;
17434         }
17435     
17436         // url (gfm)
17437         if (!this.inLink && (cap = this.rules.url.exec(src))) {
17438           src = src.substring(cap[0].length);
17439           text = escape(cap[1]);
17440           href = text;
17441           out += this.renderer.link(href, null, text);
17442           continue;
17443         }
17444     
17445         // tag
17446         if (cap = this.rules.tag.exec(src)) {
17447           if (!this.inLink && /^<a /i.test(cap[0])) {
17448             this.inLink = true;
17449           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
17450             this.inLink = false;
17451           }
17452           src = src.substring(cap[0].length);
17453           out += this.options.sanitize
17454             ? this.options.sanitizer
17455               ? this.options.sanitizer(cap[0])
17456               : escape(cap[0])
17457             : cap[0];
17458           continue;
17459         }
17460     
17461         // link
17462         if (cap = this.rules.link.exec(src)) {
17463           src = src.substring(cap[0].length);
17464           this.inLink = true;
17465           out += this.outputLink(cap, {
17466             href: cap[2],
17467             title: cap[3]
17468           });
17469           this.inLink = false;
17470           continue;
17471         }
17472     
17473         // reflink, nolink
17474         if ((cap = this.rules.reflink.exec(src))
17475             || (cap = this.rules.nolink.exec(src))) {
17476           src = src.substring(cap[0].length);
17477           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
17478           link = this.links[link.toLowerCase()];
17479           if (!link || !link.href) {
17480             out += cap[0].charAt(0);
17481             src = cap[0].substring(1) + src;
17482             continue;
17483           }
17484           this.inLink = true;
17485           out += this.outputLink(cap, link);
17486           this.inLink = false;
17487           continue;
17488         }
17489     
17490         // strong
17491         if (cap = this.rules.strong.exec(src)) {
17492           src = src.substring(cap[0].length);
17493           out += this.renderer.strong(this.output(cap[2] || cap[1]));
17494           continue;
17495         }
17496     
17497         // em
17498         if (cap = this.rules.em.exec(src)) {
17499           src = src.substring(cap[0].length);
17500           out += this.renderer.em(this.output(cap[2] || cap[1]));
17501           continue;
17502         }
17503     
17504         // code
17505         if (cap = this.rules.code.exec(src)) {
17506           src = src.substring(cap[0].length);
17507           out += this.renderer.codespan(escape(cap[2], true));
17508           continue;
17509         }
17510     
17511         // br
17512         if (cap = this.rules.br.exec(src)) {
17513           src = src.substring(cap[0].length);
17514           out += this.renderer.br();
17515           continue;
17516         }
17517     
17518         // del (gfm)
17519         if (cap = this.rules.del.exec(src)) {
17520           src = src.substring(cap[0].length);
17521           out += this.renderer.del(this.output(cap[1]));
17522           continue;
17523         }
17524     
17525         // text
17526         if (cap = this.rules.text.exec(src)) {
17527           src = src.substring(cap[0].length);
17528           out += this.renderer.text(escape(this.smartypants(cap[0])));
17529           continue;
17530         }
17531     
17532         if (src) {
17533           throw new
17534             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17535         }
17536       }
17537     
17538       return out;
17539     };
17540     
17541     /**
17542      * Compile Link
17543      */
17544     
17545     InlineLexer.prototype.outputLink = function(cap, link) {
17546       var href = escape(link.href)
17547         , title = link.title ? escape(link.title) : null;
17548     
17549       return cap[0].charAt(0) !== '!'
17550         ? this.renderer.link(href, title, this.output(cap[1]))
17551         : this.renderer.image(href, title, escape(cap[1]));
17552     };
17553     
17554     /**
17555      * Smartypants Transformations
17556      */
17557     
17558     InlineLexer.prototype.smartypants = function(text) {
17559       if (!this.options.smartypants)  { return text; }
17560       return text
17561         // em-dashes
17562         .replace(/---/g, '\u2014')
17563         // en-dashes
17564         .replace(/--/g, '\u2013')
17565         // opening singles
17566         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
17567         // closing singles & apostrophes
17568         .replace(/'/g, '\u2019')
17569         // opening doubles
17570         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
17571         // closing doubles
17572         .replace(/"/g, '\u201d')
17573         // ellipses
17574         .replace(/\.{3}/g, '\u2026');
17575     };
17576     
17577     /**
17578      * Mangle Links
17579      */
17580     
17581     InlineLexer.prototype.mangle = function(text) {
17582       if (!this.options.mangle) { return text; }
17583       var out = ''
17584         , l = text.length
17585         , i = 0
17586         , ch;
17587     
17588       for (; i < l; i++) {
17589         ch = text.charCodeAt(i);
17590         if (Math.random() > 0.5) {
17591           ch = 'x' + ch.toString(16);
17592         }
17593         out += '&#' + ch + ';';
17594       }
17595     
17596       return out;
17597     };
17598     
17599     /**
17600      * Renderer
17601      */
17602     
17603     var Renderer   = function (options) {
17604       this.options = options || {};
17605     }
17606     
17607     Renderer.prototype.code = function(code, lang, escaped) {
17608       if (this.options.highlight) {
17609         var out = this.options.highlight(code, lang);
17610         if (out != null && out !== code) {
17611           escaped = true;
17612           code = out;
17613         }
17614       } else {
17615             // hack!!! - it's already escapeD?
17616             escaped = true;
17617       }
17618     
17619       if (!lang) {
17620         return '<pre><code>'
17621           + (escaped ? code : escape(code, true))
17622           + '\n</code></pre>';
17623       }
17624     
17625       return '<pre><code class="'
17626         + this.options.langPrefix
17627         + escape(lang, true)
17628         + '">'
17629         + (escaped ? code : escape(code, true))
17630         + '\n</code></pre>\n';
17631     };
17632     
17633     Renderer.prototype.blockquote = function(quote) {
17634       return '<blockquote>\n' + quote + '</blockquote>\n';
17635     };
17636     
17637     Renderer.prototype.html = function(html) {
17638       return html;
17639     };
17640     
17641     Renderer.prototype.heading = function(text, level, raw) {
17642       return '<h'
17643         + level
17644         + ' id="'
17645         + this.options.headerPrefix
17646         + raw.toLowerCase().replace(/[^\w]+/g, '-')
17647         + '">'
17648         + text
17649         + '</h'
17650         + level
17651         + '>\n';
17652     };
17653     
17654     Renderer.prototype.hr = function() {
17655       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
17656     };
17657     
17658     Renderer.prototype.list = function(body, ordered) {
17659       var type = ordered ? 'ol' : 'ul';
17660       return '<' + type + '>\n' + body + '</' + type + '>\n';
17661     };
17662     
17663     Renderer.prototype.listitem = function(text) {
17664       return '<li>' + text + '</li>\n';
17665     };
17666     
17667     Renderer.prototype.paragraph = function(text) {
17668       return '<p>' + text + '</p>\n';
17669     };
17670     
17671     Renderer.prototype.table = function(header, body) {
17672       return '<table class="table table-striped">\n'
17673         + '<thead>\n'
17674         + header
17675         + '</thead>\n'
17676         + '<tbody>\n'
17677         + body
17678         + '</tbody>\n'
17679         + '</table>\n';
17680     };
17681     
17682     Renderer.prototype.tablerow = function(content) {
17683       return '<tr>\n' + content + '</tr>\n';
17684     };
17685     
17686     Renderer.prototype.tablecell = function(content, flags) {
17687       var type = flags.header ? 'th' : 'td';
17688       var tag = flags.align
17689         ? '<' + type + ' style="text-align:' + flags.align + '">'
17690         : '<' + type + '>';
17691       return tag + content + '</' + type + '>\n';
17692     };
17693     
17694     // span level renderer
17695     Renderer.prototype.strong = function(text) {
17696       return '<strong>' + text + '</strong>';
17697     };
17698     
17699     Renderer.prototype.em = function(text) {
17700       return '<em>' + text + '</em>';
17701     };
17702     
17703     Renderer.prototype.codespan = function(text) {
17704       return '<code>' + text + '</code>';
17705     };
17706     
17707     Renderer.prototype.br = function() {
17708       return this.options.xhtml ? '<br/>' : '<br>';
17709     };
17710     
17711     Renderer.prototype.del = function(text) {
17712       return '<del>' + text + '</del>';
17713     };
17714     
17715     Renderer.prototype.link = function(href, title, text) {
17716       if (this.options.sanitize) {
17717         try {
17718           var prot = decodeURIComponent(unescape(href))
17719             .replace(/[^\w:]/g, '')
17720             .toLowerCase();
17721         } catch (e) {
17722           return '';
17723         }
17724         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
17725           return '';
17726         }
17727       }
17728       var out = '<a href="' + href + '"';
17729       if (title) {
17730         out += ' title="' + title + '"';
17731       }
17732       out += '>' + text + '</a>';
17733       return out;
17734     };
17735     
17736     Renderer.prototype.image = function(href, title, text) {
17737       var out = '<img src="' + href + '" alt="' + text + '"';
17738       if (title) {
17739         out += ' title="' + title + '"';
17740       }
17741       out += this.options.xhtml ? '/>' : '>';
17742       return out;
17743     };
17744     
17745     Renderer.prototype.text = function(text) {
17746       return text;
17747     };
17748     
17749     /**
17750      * Parsing & Compiling
17751      */
17752     
17753     var Parser= function (options) {
17754       this.tokens = [];
17755       this.token = null;
17756       this.options = options || marked.defaults;
17757       this.options.renderer = this.options.renderer || new Renderer;
17758       this.renderer = this.options.renderer;
17759       this.renderer.options = this.options;
17760     }
17761     
17762     /**
17763      * Static Parse Method
17764      */
17765     
17766     Parser.parse = function(src, options, renderer) {
17767       var parser = new Parser(options, renderer);
17768       return parser.parse(src);
17769     };
17770     
17771     /**
17772      * Parse Loop
17773      */
17774     
17775     Parser.prototype.parse = function(src) {
17776       this.inline = new InlineLexer(src.links, this.options, this.renderer);
17777       this.tokens = src.reverse();
17778     
17779       var out = '';
17780       while (this.next()) {
17781         out += this.tok();
17782       }
17783     
17784       return out;
17785     };
17786     
17787     /**
17788      * Next Token
17789      */
17790     
17791     Parser.prototype.next = function() {
17792       return this.token = this.tokens.pop();
17793     };
17794     
17795     /**
17796      * Preview Next Token
17797      */
17798     
17799     Parser.prototype.peek = function() {
17800       return this.tokens[this.tokens.length - 1] || 0;
17801     };
17802     
17803     /**
17804      * Parse Text Tokens
17805      */
17806     
17807     Parser.prototype.parseText = function() {
17808       var body = this.token.text;
17809     
17810       while (this.peek().type === 'text') {
17811         body += '\n' + this.next().text;
17812       }
17813     
17814       return this.inline.output(body);
17815     };
17816     
17817     /**
17818      * Parse Current Token
17819      */
17820     
17821     Parser.prototype.tok = function() {
17822       switch (this.token.type) {
17823         case 'space': {
17824           return '';
17825         }
17826         case 'hr': {
17827           return this.renderer.hr();
17828         }
17829         case 'heading': {
17830           return this.renderer.heading(
17831             this.inline.output(this.token.text),
17832             this.token.depth,
17833             this.token.text);
17834         }
17835         case 'code': {
17836           return this.renderer.code(this.token.text,
17837             this.token.lang,
17838             this.token.escaped);
17839         }
17840         case 'table': {
17841           var header = ''
17842             , body = ''
17843             , i
17844             , row
17845             , cell
17846             , flags
17847             , j;
17848     
17849           // header
17850           cell = '';
17851           for (i = 0; i < this.token.header.length; i++) {
17852             flags = { header: true, align: this.token.align[i] };
17853             cell += this.renderer.tablecell(
17854               this.inline.output(this.token.header[i]),
17855               { header: true, align: this.token.align[i] }
17856             );
17857           }
17858           header += this.renderer.tablerow(cell);
17859     
17860           for (i = 0; i < this.token.cells.length; i++) {
17861             row = this.token.cells[i];
17862     
17863             cell = '';
17864             for (j = 0; j < row.length; j++) {
17865               cell += this.renderer.tablecell(
17866                 this.inline.output(row[j]),
17867                 { header: false, align: this.token.align[j] }
17868               );
17869             }
17870     
17871             body += this.renderer.tablerow(cell);
17872           }
17873           return this.renderer.table(header, body);
17874         }
17875         case 'blockquote_start': {
17876           var body = '';
17877     
17878           while (this.next().type !== 'blockquote_end') {
17879             body += this.tok();
17880           }
17881     
17882           return this.renderer.blockquote(body);
17883         }
17884         case 'list_start': {
17885           var body = ''
17886             , ordered = this.token.ordered;
17887     
17888           while (this.next().type !== 'list_end') {
17889             body += this.tok();
17890           }
17891     
17892           return this.renderer.list(body, ordered);
17893         }
17894         case 'list_item_start': {
17895           var body = '';
17896     
17897           while (this.next().type !== 'list_item_end') {
17898             body += this.token.type === 'text'
17899               ? this.parseText()
17900               : this.tok();
17901           }
17902     
17903           return this.renderer.listitem(body);
17904         }
17905         case 'loose_item_start': {
17906           var body = '';
17907     
17908           while (this.next().type !== 'list_item_end') {
17909             body += this.tok();
17910           }
17911     
17912           return this.renderer.listitem(body);
17913         }
17914         case 'html': {
17915           var html = !this.token.pre && !this.options.pedantic
17916             ? this.inline.output(this.token.text)
17917             : this.token.text;
17918           return this.renderer.html(html);
17919         }
17920         case 'paragraph': {
17921           return this.renderer.paragraph(this.inline.output(this.token.text));
17922         }
17923         case 'text': {
17924           return this.renderer.paragraph(this.parseText());
17925         }
17926       }
17927     };
17928   
17929     
17930     var noop = function () {}
17931     noop.exec = noop;
17932     
17933     var merge = function (obj) {
17934       var i = 1
17935         , target
17936         , key;
17937     
17938       for (; i < arguments.length; i++) {
17939         target = arguments[i];
17940         for (key in target) {
17941           if (Object.prototype.hasOwnProperty.call(target, key)) {
17942             obj[key] = target[key];
17943           }
17944         }
17945       }
17946     
17947       return obj;
17948     }
17949     
17950     
17951     /**
17952      * Marked
17953      */
17954     
17955     var marked = function (src, opt, callback) {
17956       if (callback || typeof opt === 'function') {
17957         if (!callback) {
17958           callback = opt;
17959           opt = null;
17960         }
17961     
17962         opt = merge({}, marked.defaults, opt || {});
17963     
17964         var highlight = opt.highlight
17965           , tokens
17966           , pending
17967           , i = 0;
17968     
17969         try {
17970           tokens = Lexer.lex(src, opt)
17971         } catch (e) {
17972           return callback(e);
17973         }
17974     
17975         pending = tokens.length;
17976     
17977         var done = function(err) {
17978           if (err) {
17979             opt.highlight = highlight;
17980             return callback(err);
17981           }
17982     
17983           var out;
17984     
17985           try {
17986             out = Parser.parse(tokens, opt);
17987           } catch (e) {
17988             err = e;
17989           }
17990     
17991           opt.highlight = highlight;
17992     
17993           return err
17994             ? callback(err)
17995             : callback(null, out);
17996         };
17997     
17998         if (!highlight || highlight.length < 3) {
17999           return done();
18000         }
18001     
18002         delete opt.highlight;
18003     
18004         if (!pending) { return done(); }
18005     
18006         for (; i < tokens.length; i++) {
18007           (function(token) {
18008             if (token.type !== 'code') {
18009               return --pending || done();
18010             }
18011             return highlight(token.text, token.lang, function(err, code) {
18012               if (err) { return done(err); }
18013               if (code == null || code === token.text) {
18014                 return --pending || done();
18015               }
18016               token.text = code;
18017               token.escaped = true;
18018               --pending || done();
18019             });
18020           })(tokens[i]);
18021         }
18022     
18023         return;
18024       }
18025       try {
18026         if (opt) { opt = merge({}, marked.defaults, opt); }
18027         return Parser.parse(Lexer.lex(src, opt), opt);
18028       } catch (e) {
18029         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18030         if ((opt || marked.defaults).silent) {
18031           return '<p>An error occured:</p><pre>'
18032             + escape(e.message + '', true)
18033             + '</pre>';
18034         }
18035         throw e;
18036       }
18037     }
18038     
18039     /**
18040      * Options
18041      */
18042     
18043     marked.options =
18044     marked.setOptions = function(opt) {
18045       merge(marked.defaults, opt);
18046       return marked;
18047     };
18048     
18049     marked.defaults = {
18050       gfm: true,
18051       tables: true,
18052       breaks: false,
18053       pedantic: false,
18054       sanitize: false,
18055       sanitizer: null,
18056       mangle: true,
18057       smartLists: false,
18058       silent: false,
18059       highlight: null,
18060       langPrefix: 'lang-',
18061       smartypants: false,
18062       headerPrefix: '',
18063       renderer: new Renderer,
18064       xhtml: false
18065     };
18066     
18067     /**
18068      * Expose
18069      */
18070     
18071     marked.Parser = Parser;
18072     marked.parser = Parser.parse;
18073     
18074     marked.Renderer = Renderer;
18075     
18076     marked.Lexer = Lexer;
18077     marked.lexer = Lexer.lex;
18078     
18079     marked.InlineLexer = InlineLexer;
18080     marked.inlineLexer = InlineLexer.output;
18081     
18082     marked.parse = marked;
18083     
18084     Roo.Markdown.marked = marked;
18085
18086 })();/*
18087  * Based on:
18088  * Ext JS Library 1.1.1
18089  * Copyright(c) 2006-2007, Ext JS, LLC.
18090  *
18091  * Originally Released Under LGPL - original licence link has changed is not relivant.
18092  *
18093  * Fork - LGPL
18094  * <script type="text/javascript">
18095  */
18096
18097
18098
18099 /*
18100  * These classes are derivatives of the similarly named classes in the YUI Library.
18101  * The original license:
18102  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18103  * Code licensed under the BSD License:
18104  * http://developer.yahoo.net/yui/license.txt
18105  */
18106
18107 (function() {
18108
18109 var Event=Roo.EventManager;
18110 var Dom=Roo.lib.Dom;
18111
18112 /**
18113  * @class Roo.dd.DragDrop
18114  * @extends Roo.util.Observable
18115  * Defines the interface and base operation of items that that can be
18116  * dragged or can be drop targets.  It was designed to be extended, overriding
18117  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18118  * Up to three html elements can be associated with a DragDrop instance:
18119  * <ul>
18120  * <li>linked element: the element that is passed into the constructor.
18121  * This is the element which defines the boundaries for interaction with
18122  * other DragDrop objects.</li>
18123  * <li>handle element(s): The drag operation only occurs if the element that
18124  * was clicked matches a handle element.  By default this is the linked
18125  * element, but there are times that you will want only a portion of the
18126  * linked element to initiate the drag operation, and the setHandleElId()
18127  * method provides a way to define this.</li>
18128  * <li>drag element: this represents the element that would be moved along
18129  * with the cursor during a drag operation.  By default, this is the linked
18130  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18131  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18132  * </li>
18133  * </ul>
18134  * This class should not be instantiated until the onload event to ensure that
18135  * the associated elements are available.
18136  * The following would define a DragDrop obj that would interact with any
18137  * other DragDrop obj in the "group1" group:
18138  * <pre>
18139  *  dd = new Roo.dd.DragDrop("div1", "group1");
18140  * </pre>
18141  * Since none of the event handlers have been implemented, nothing would
18142  * actually happen if you were to run the code above.  Normally you would
18143  * override this class or one of the default implementations, but you can
18144  * also override the methods you want on an instance of the class...
18145  * <pre>
18146  *  dd.onDragDrop = function(e, id) {
18147  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18148  *  }
18149  * </pre>
18150  * @constructor
18151  * @param {String} id of the element that is linked to this instance
18152  * @param {String} sGroup the group of related DragDrop objects
18153  * @param {object} config an object containing configurable attributes
18154  *                Valid properties for DragDrop:
18155  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18156  */
18157 Roo.dd.DragDrop = function(id, sGroup, config) {
18158     if (id) {
18159         this.init(id, sGroup, config);
18160     }
18161     
18162 };
18163
18164 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18165
18166     /**
18167      * The id of the element associated with this object.  This is what we
18168      * refer to as the "linked element" because the size and position of
18169      * this element is used to determine when the drag and drop objects have
18170      * interacted.
18171      * @property id
18172      * @type String
18173      */
18174     id: null,
18175
18176     /**
18177      * Configuration attributes passed into the constructor
18178      * @property config
18179      * @type object
18180      */
18181     config: null,
18182
18183     /**
18184      * The id of the element that will be dragged.  By default this is same
18185      * as the linked element , but could be changed to another element. Ex:
18186      * Roo.dd.DDProxy
18187      * @property dragElId
18188      * @type String
18189      * @private
18190      */
18191     dragElId: null,
18192
18193     /**
18194      * the id of the element that initiates the drag operation.  By default
18195      * this is the linked element, but could be changed to be a child of this
18196      * element.  This lets us do things like only starting the drag when the
18197      * header element within the linked html element is clicked.
18198      * @property handleElId
18199      * @type String
18200      * @private
18201      */
18202     handleElId: null,
18203
18204     /**
18205      * An associative array of HTML tags that will be ignored if clicked.
18206      * @property invalidHandleTypes
18207      * @type {string: string}
18208      */
18209     invalidHandleTypes: null,
18210
18211     /**
18212      * An associative array of ids for elements that will be ignored if clicked
18213      * @property invalidHandleIds
18214      * @type {string: string}
18215      */
18216     invalidHandleIds: null,
18217
18218     /**
18219      * An indexted array of css class names for elements that will be ignored
18220      * if clicked.
18221      * @property invalidHandleClasses
18222      * @type string[]
18223      */
18224     invalidHandleClasses: null,
18225
18226     /**
18227      * The linked element's absolute X position at the time the drag was
18228      * started
18229      * @property startPageX
18230      * @type int
18231      * @private
18232      */
18233     startPageX: 0,
18234
18235     /**
18236      * The linked element's absolute X position at the time the drag was
18237      * started
18238      * @property startPageY
18239      * @type int
18240      * @private
18241      */
18242     startPageY: 0,
18243
18244     /**
18245      * The group defines a logical collection of DragDrop objects that are
18246      * related.  Instances only get events when interacting with other
18247      * DragDrop object in the same group.  This lets us define multiple
18248      * groups using a single DragDrop subclass if we want.
18249      * @property groups
18250      * @type {string: string}
18251      */
18252     groups: null,
18253
18254     /**
18255      * Individual drag/drop instances can be locked.  This will prevent
18256      * onmousedown start drag.
18257      * @property locked
18258      * @type boolean
18259      * @private
18260      */
18261     locked: false,
18262
18263     /**
18264      * Lock this instance
18265      * @method lock
18266      */
18267     lock: function() { this.locked = true; },
18268
18269     /**
18270      * Unlock this instace
18271      * @method unlock
18272      */
18273     unlock: function() { this.locked = false; },
18274
18275     /**
18276      * By default, all insances can be a drop target.  This can be disabled by
18277      * setting isTarget to false.
18278      * @method isTarget
18279      * @type boolean
18280      */
18281     isTarget: true,
18282
18283     /**
18284      * The padding configured for this drag and drop object for calculating
18285      * the drop zone intersection with this object.
18286      * @method padding
18287      * @type int[]
18288      */
18289     padding: null,
18290
18291     /**
18292      * Cached reference to the linked element
18293      * @property _domRef
18294      * @private
18295      */
18296     _domRef: null,
18297
18298     /**
18299      * Internal typeof flag
18300      * @property __ygDragDrop
18301      * @private
18302      */
18303     __ygDragDrop: true,
18304
18305     /**
18306      * Set to true when horizontal contraints are applied
18307      * @property constrainX
18308      * @type boolean
18309      * @private
18310      */
18311     constrainX: false,
18312
18313     /**
18314      * Set to true when vertical contraints are applied
18315      * @property constrainY
18316      * @type boolean
18317      * @private
18318      */
18319     constrainY: false,
18320
18321     /**
18322      * The left constraint
18323      * @property minX
18324      * @type int
18325      * @private
18326      */
18327     minX: 0,
18328
18329     /**
18330      * The right constraint
18331      * @property maxX
18332      * @type int
18333      * @private
18334      */
18335     maxX: 0,
18336
18337     /**
18338      * The up constraint
18339      * @property minY
18340      * @type int
18341      * @type int
18342      * @private
18343      */
18344     minY: 0,
18345
18346     /**
18347      * The down constraint
18348      * @property maxY
18349      * @type int
18350      * @private
18351      */
18352     maxY: 0,
18353
18354     /**
18355      * Maintain offsets when we resetconstraints.  Set to true when you want
18356      * the position of the element relative to its parent to stay the same
18357      * when the page changes
18358      *
18359      * @property maintainOffset
18360      * @type boolean
18361      */
18362     maintainOffset: false,
18363
18364     /**
18365      * Array of pixel locations the element will snap to if we specified a
18366      * horizontal graduation/interval.  This array is generated automatically
18367      * when you define a tick interval.
18368      * @property xTicks
18369      * @type int[]
18370      */
18371     xTicks: null,
18372
18373     /**
18374      * Array of pixel locations the element will snap to if we specified a
18375      * vertical graduation/interval.  This array is generated automatically
18376      * when you define a tick interval.
18377      * @property yTicks
18378      * @type int[]
18379      */
18380     yTicks: null,
18381
18382     /**
18383      * By default the drag and drop instance will only respond to the primary
18384      * button click (left button for a right-handed mouse).  Set to true to
18385      * allow drag and drop to start with any mouse click that is propogated
18386      * by the browser
18387      * @property primaryButtonOnly
18388      * @type boolean
18389      */
18390     primaryButtonOnly: true,
18391
18392     /**
18393      * The availabe property is false until the linked dom element is accessible.
18394      * @property available
18395      * @type boolean
18396      */
18397     available: false,
18398
18399     /**
18400      * By default, drags can only be initiated if the mousedown occurs in the
18401      * region the linked element is.  This is done in part to work around a
18402      * bug in some browsers that mis-report the mousedown if the previous
18403      * mouseup happened outside of the window.  This property is set to true
18404      * if outer handles are defined.
18405      *
18406      * @property hasOuterHandles
18407      * @type boolean
18408      * @default false
18409      */
18410     hasOuterHandles: false,
18411
18412     /**
18413      * Code that executes immediately before the startDrag event
18414      * @method b4StartDrag
18415      * @private
18416      */
18417     b4StartDrag: function(x, y) { },
18418
18419     /**
18420      * Abstract method called after a drag/drop object is clicked
18421      * and the drag or mousedown time thresholds have beeen met.
18422      * @method startDrag
18423      * @param {int} X click location
18424      * @param {int} Y click location
18425      */
18426     startDrag: function(x, y) { /* override this */ },
18427
18428     /**
18429      * Code that executes immediately before the onDrag event
18430      * @method b4Drag
18431      * @private
18432      */
18433     b4Drag: function(e) { },
18434
18435     /**
18436      * Abstract method called during the onMouseMove event while dragging an
18437      * object.
18438      * @method onDrag
18439      * @param {Event} e the mousemove event
18440      */
18441     onDrag: function(e) { /* override this */ },
18442
18443     /**
18444      * Abstract method called when this element fist begins hovering over
18445      * another DragDrop obj
18446      * @method onDragEnter
18447      * @param {Event} e the mousemove event
18448      * @param {String|DragDrop[]} id In POINT mode, the element
18449      * id this is hovering over.  In INTERSECT mode, an array of one or more
18450      * dragdrop items being hovered over.
18451      */
18452     onDragEnter: function(e, id) { /* override this */ },
18453
18454     /**
18455      * Code that executes immediately before the onDragOver event
18456      * @method b4DragOver
18457      * @private
18458      */
18459     b4DragOver: function(e) { },
18460
18461     /**
18462      * Abstract method called when this element is hovering over another
18463      * DragDrop obj
18464      * @method onDragOver
18465      * @param {Event} e the mousemove event
18466      * @param {String|DragDrop[]} id In POINT mode, the element
18467      * id this is hovering over.  In INTERSECT mode, an array of dd items
18468      * being hovered over.
18469      */
18470     onDragOver: function(e, id) { /* override this */ },
18471
18472     /**
18473      * Code that executes immediately before the onDragOut event
18474      * @method b4DragOut
18475      * @private
18476      */
18477     b4DragOut: function(e) { },
18478
18479     /**
18480      * Abstract method called when we are no longer hovering over an element
18481      * @method onDragOut
18482      * @param {Event} e the mousemove event
18483      * @param {String|DragDrop[]} id In POINT mode, the element
18484      * id this was hovering over.  In INTERSECT mode, an array of dd items
18485      * that the mouse is no longer over.
18486      */
18487     onDragOut: function(e, id) { /* override this */ },
18488
18489     /**
18490      * Code that executes immediately before the onDragDrop event
18491      * @method b4DragDrop
18492      * @private
18493      */
18494     b4DragDrop: function(e) { },
18495
18496     /**
18497      * Abstract method called when this item is dropped on another DragDrop
18498      * obj
18499      * @method onDragDrop
18500      * @param {Event} e the mouseup event
18501      * @param {String|DragDrop[]} id In POINT mode, the element
18502      * id this was dropped on.  In INTERSECT mode, an array of dd items this
18503      * was dropped on.
18504      */
18505     onDragDrop: function(e, id) { /* override this */ },
18506
18507     /**
18508      * Abstract method called when this item is dropped on an area with no
18509      * drop target
18510      * @method onInvalidDrop
18511      * @param {Event} e the mouseup event
18512      */
18513     onInvalidDrop: function(e) { /* override this */ },
18514
18515     /**
18516      * Code that executes immediately before the endDrag event
18517      * @method b4EndDrag
18518      * @private
18519      */
18520     b4EndDrag: function(e) { },
18521
18522     /**
18523      * Fired when we are done dragging the object
18524      * @method endDrag
18525      * @param {Event} e the mouseup event
18526      */
18527     endDrag: function(e) { /* override this */ },
18528
18529     /**
18530      * Code executed immediately before the onMouseDown event
18531      * @method b4MouseDown
18532      * @param {Event} e the mousedown event
18533      * @private
18534      */
18535     b4MouseDown: function(e) {  },
18536
18537     /**
18538      * Event handler that fires when a drag/drop obj gets a mousedown
18539      * @method onMouseDown
18540      * @param {Event} e the mousedown event
18541      */
18542     onMouseDown: function(e) { /* override this */ },
18543
18544     /**
18545      * Event handler that fires when a drag/drop obj gets a mouseup
18546      * @method onMouseUp
18547      * @param {Event} e the mouseup event
18548      */
18549     onMouseUp: function(e) { /* override this */ },
18550
18551     /**
18552      * Override the onAvailable method to do what is needed after the initial
18553      * position was determined.
18554      * @method onAvailable
18555      */
18556     onAvailable: function () {
18557     },
18558
18559     /*
18560      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
18561      * @type Object
18562      */
18563     defaultPadding : {left:0, right:0, top:0, bottom:0},
18564
18565     /*
18566      * Initializes the drag drop object's constraints to restrict movement to a certain element.
18567  *
18568  * Usage:
18569  <pre><code>
18570  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
18571                 { dragElId: "existingProxyDiv" });
18572  dd.startDrag = function(){
18573      this.constrainTo("parent-id");
18574  };
18575  </code></pre>
18576  * Or you can initalize it using the {@link Roo.Element} object:
18577  <pre><code>
18578  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
18579      startDrag : function(){
18580          this.constrainTo("parent-id");
18581      }
18582  });
18583  </code></pre>
18584      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
18585      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
18586      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
18587      * an object containing the sides to pad. For example: {right:10, bottom:10}
18588      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
18589      */
18590     constrainTo : function(constrainTo, pad, inContent){
18591         if(typeof pad == "number"){
18592             pad = {left: pad, right:pad, top:pad, bottom:pad};
18593         }
18594         pad = pad || this.defaultPadding;
18595         var b = Roo.get(this.getEl()).getBox();
18596         var ce = Roo.get(constrainTo);
18597         var s = ce.getScroll();
18598         var c, cd = ce.dom;
18599         if(cd == document.body){
18600             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
18601         }else{
18602             xy = ce.getXY();
18603             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
18604         }
18605
18606
18607         var topSpace = b.y - c.y;
18608         var leftSpace = b.x - c.x;
18609
18610         this.resetConstraints();
18611         this.setXConstraint(leftSpace - (pad.left||0), // left
18612                 c.width - leftSpace - b.width - (pad.right||0) //right
18613         );
18614         this.setYConstraint(topSpace - (pad.top||0), //top
18615                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
18616         );
18617     },
18618
18619     /**
18620      * Returns a reference to the linked element
18621      * @method getEl
18622      * @return {HTMLElement} the html element
18623      */
18624     getEl: function() {
18625         if (!this._domRef) {
18626             this._domRef = Roo.getDom(this.id);
18627         }
18628
18629         return this._domRef;
18630     },
18631
18632     /**
18633      * Returns a reference to the actual element to drag.  By default this is
18634      * the same as the html element, but it can be assigned to another
18635      * element. An example of this can be found in Roo.dd.DDProxy
18636      * @method getDragEl
18637      * @return {HTMLElement} the html element
18638      */
18639     getDragEl: function() {
18640         return Roo.getDom(this.dragElId);
18641     },
18642
18643     /**
18644      * Sets up the DragDrop object.  Must be called in the constructor of any
18645      * Roo.dd.DragDrop subclass
18646      * @method init
18647      * @param id the id of the linked element
18648      * @param {String} sGroup the group of related items
18649      * @param {object} config configuration attributes
18650      */
18651     init: function(id, sGroup, config) {
18652         this.initTarget(id, sGroup, config);
18653         if (!Roo.isTouch) {
18654             Event.on(this.id, "mousedown", this.handleMouseDown, this);
18655         }
18656         Event.on(this.id, "touchstart", this.handleMouseDown, this);
18657         // Event.on(this.id, "selectstart", Event.preventDefault);
18658     },
18659
18660     /**
18661      * Initializes Targeting functionality only... the object does not
18662      * get a mousedown handler.
18663      * @method initTarget
18664      * @param id the id of the linked element
18665      * @param {String} sGroup the group of related items
18666      * @param {object} config configuration attributes
18667      */
18668     initTarget: function(id, sGroup, config) {
18669
18670         // configuration attributes
18671         this.config = config || {};
18672
18673         // create a local reference to the drag and drop manager
18674         this.DDM = Roo.dd.DDM;
18675         // initialize the groups array
18676         this.groups = {};
18677
18678         // assume that we have an element reference instead of an id if the
18679         // parameter is not a string
18680         if (typeof id !== "string") {
18681             id = Roo.id(id);
18682         }
18683
18684         // set the id
18685         this.id = id;
18686
18687         // add to an interaction group
18688         this.addToGroup((sGroup) ? sGroup : "default");
18689
18690         // We don't want to register this as the handle with the manager
18691         // so we just set the id rather than calling the setter.
18692         this.handleElId = id;
18693
18694         // the linked element is the element that gets dragged by default
18695         this.setDragElId(id);
18696
18697         // by default, clicked anchors will not start drag operations.
18698         this.invalidHandleTypes = { A: "A" };
18699         this.invalidHandleIds = {};
18700         this.invalidHandleClasses = [];
18701
18702         this.applyConfig();
18703
18704         this.handleOnAvailable();
18705     },
18706
18707     /**
18708      * Applies the configuration parameters that were passed into the constructor.
18709      * This is supposed to happen at each level through the inheritance chain.  So
18710      * a DDProxy implentation will execute apply config on DDProxy, DD, and
18711      * DragDrop in order to get all of the parameters that are available in
18712      * each object.
18713      * @method applyConfig
18714      */
18715     applyConfig: function() {
18716
18717         // configurable properties:
18718         //    padding, isTarget, maintainOffset, primaryButtonOnly
18719         this.padding           = this.config.padding || [0, 0, 0, 0];
18720         this.isTarget          = (this.config.isTarget !== false);
18721         this.maintainOffset    = (this.config.maintainOffset);
18722         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
18723
18724     },
18725
18726     /**
18727      * Executed when the linked element is available
18728      * @method handleOnAvailable
18729      * @private
18730      */
18731     handleOnAvailable: function() {
18732         this.available = true;
18733         this.resetConstraints();
18734         this.onAvailable();
18735     },
18736
18737      /**
18738      * Configures the padding for the target zone in px.  Effectively expands
18739      * (or reduces) the virtual object size for targeting calculations.
18740      * Supports css-style shorthand; if only one parameter is passed, all sides
18741      * will have that padding, and if only two are passed, the top and bottom
18742      * will have the first param, the left and right the second.
18743      * @method setPadding
18744      * @param {int} iTop    Top pad
18745      * @param {int} iRight  Right pad
18746      * @param {int} iBot    Bot pad
18747      * @param {int} iLeft   Left pad
18748      */
18749     setPadding: function(iTop, iRight, iBot, iLeft) {
18750         // this.padding = [iLeft, iRight, iTop, iBot];
18751         if (!iRight && 0 !== iRight) {
18752             this.padding = [iTop, iTop, iTop, iTop];
18753         } else if (!iBot && 0 !== iBot) {
18754             this.padding = [iTop, iRight, iTop, iRight];
18755         } else {
18756             this.padding = [iTop, iRight, iBot, iLeft];
18757         }
18758     },
18759
18760     /**
18761      * Stores the initial placement of the linked element.
18762      * @method setInitialPosition
18763      * @param {int} diffX   the X offset, default 0
18764      * @param {int} diffY   the Y offset, default 0
18765      */
18766     setInitPosition: function(diffX, diffY) {
18767         var el = this.getEl();
18768
18769         if (!this.DDM.verifyEl(el)) {
18770             return;
18771         }
18772
18773         var dx = diffX || 0;
18774         var dy = diffY || 0;
18775
18776         var p = Dom.getXY( el );
18777
18778         this.initPageX = p[0] - dx;
18779         this.initPageY = p[1] - dy;
18780
18781         this.lastPageX = p[0];
18782         this.lastPageY = p[1];
18783
18784
18785         this.setStartPosition(p);
18786     },
18787
18788     /**
18789      * Sets the start position of the element.  This is set when the obj
18790      * is initialized, the reset when a drag is started.
18791      * @method setStartPosition
18792      * @param pos current position (from previous lookup)
18793      * @private
18794      */
18795     setStartPosition: function(pos) {
18796         var p = pos || Dom.getXY( this.getEl() );
18797         this.deltaSetXY = null;
18798
18799         this.startPageX = p[0];
18800         this.startPageY = p[1];
18801     },
18802
18803     /**
18804      * Add this instance to a group of related drag/drop objects.  All
18805      * instances belong to at least one group, and can belong to as many
18806      * groups as needed.
18807      * @method addToGroup
18808      * @param sGroup {string} the name of the group
18809      */
18810     addToGroup: function(sGroup) {
18811         this.groups[sGroup] = true;
18812         this.DDM.regDragDrop(this, sGroup);
18813     },
18814
18815     /**
18816      * Remove's this instance from the supplied interaction group
18817      * @method removeFromGroup
18818      * @param {string}  sGroup  The group to drop
18819      */
18820     removeFromGroup: function(sGroup) {
18821         if (this.groups[sGroup]) {
18822             delete this.groups[sGroup];
18823         }
18824
18825         this.DDM.removeDDFromGroup(this, sGroup);
18826     },
18827
18828     /**
18829      * Allows you to specify that an element other than the linked element
18830      * will be moved with the cursor during a drag
18831      * @method setDragElId
18832      * @param id {string} the id of the element that will be used to initiate the drag
18833      */
18834     setDragElId: function(id) {
18835         this.dragElId = id;
18836     },
18837
18838     /**
18839      * Allows you to specify a child of the linked element that should be
18840      * used to initiate the drag operation.  An example of this would be if
18841      * you have a content div with text and links.  Clicking anywhere in the
18842      * content area would normally start the drag operation.  Use this method
18843      * to specify that an element inside of the content div is the element
18844      * that starts the drag operation.
18845      * @method setHandleElId
18846      * @param id {string} the id of the element that will be used to
18847      * initiate the drag.
18848      */
18849     setHandleElId: function(id) {
18850         if (typeof id !== "string") {
18851             id = Roo.id(id);
18852         }
18853         this.handleElId = id;
18854         this.DDM.regHandle(this.id, id);
18855     },
18856
18857     /**
18858      * Allows you to set an element outside of the linked element as a drag
18859      * handle
18860      * @method setOuterHandleElId
18861      * @param id the id of the element that will be used to initiate the drag
18862      */
18863     setOuterHandleElId: function(id) {
18864         if (typeof id !== "string") {
18865             id = Roo.id(id);
18866         }
18867         Event.on(id, "mousedown",
18868                 this.handleMouseDown, this);
18869         this.setHandleElId(id);
18870
18871         this.hasOuterHandles = true;
18872     },
18873
18874     /**
18875      * Remove all drag and drop hooks for this element
18876      * @method unreg
18877      */
18878     unreg: function() {
18879         Event.un(this.id, "mousedown",
18880                 this.handleMouseDown);
18881         Event.un(this.id, "touchstart",
18882                 this.handleMouseDown);
18883         this._domRef = null;
18884         this.DDM._remove(this);
18885     },
18886
18887     destroy : function(){
18888         this.unreg();
18889     },
18890
18891     /**
18892      * Returns true if this instance is locked, or the drag drop mgr is locked
18893      * (meaning that all drag/drop is disabled on the page.)
18894      * @method isLocked
18895      * @return {boolean} true if this obj or all drag/drop is locked, else
18896      * false
18897      */
18898     isLocked: function() {
18899         return (this.DDM.isLocked() || this.locked);
18900     },
18901
18902     /**
18903      * Fired when this object is clicked
18904      * @method handleMouseDown
18905      * @param {Event} e
18906      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
18907      * @private
18908      */
18909     handleMouseDown: function(e, oDD){
18910      
18911         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
18912             //Roo.log('not touch/ button !=0');
18913             return;
18914         }
18915         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
18916             return; // double touch..
18917         }
18918         
18919
18920         if (this.isLocked()) {
18921             //Roo.log('locked');
18922             return;
18923         }
18924
18925         this.DDM.refreshCache(this.groups);
18926 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
18927         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
18928         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
18929             //Roo.log('no outer handes or not over target');
18930                 // do nothing.
18931         } else {
18932 //            Roo.log('check validator');
18933             if (this.clickValidator(e)) {
18934 //                Roo.log('validate success');
18935                 // set the initial element position
18936                 this.setStartPosition();
18937
18938
18939                 this.b4MouseDown(e);
18940                 this.onMouseDown(e);
18941
18942                 this.DDM.handleMouseDown(e, this);
18943
18944                 this.DDM.stopEvent(e);
18945             } else {
18946
18947
18948             }
18949         }
18950     },
18951
18952     clickValidator: function(e) {
18953         var target = e.getTarget();
18954         return ( this.isValidHandleChild(target) &&
18955                     (this.id == this.handleElId ||
18956                         this.DDM.handleWasClicked(target, this.id)) );
18957     },
18958
18959     /**
18960      * Allows you to specify a tag name that should not start a drag operation
18961      * when clicked.  This is designed to facilitate embedding links within a
18962      * drag handle that do something other than start the drag.
18963      * @method addInvalidHandleType
18964      * @param {string} tagName the type of element to exclude
18965      */
18966     addInvalidHandleType: function(tagName) {
18967         var type = tagName.toUpperCase();
18968         this.invalidHandleTypes[type] = type;
18969     },
18970
18971     /**
18972      * Lets you to specify an element id for a child of a drag handle
18973      * that should not initiate a drag
18974      * @method addInvalidHandleId
18975      * @param {string} id the element id of the element you wish to ignore
18976      */
18977     addInvalidHandleId: function(id) {
18978         if (typeof id !== "string") {
18979             id = Roo.id(id);
18980         }
18981         this.invalidHandleIds[id] = id;
18982     },
18983
18984     /**
18985      * Lets you specify a css class of elements that will not initiate a drag
18986      * @method addInvalidHandleClass
18987      * @param {string} cssClass the class of the elements you wish to ignore
18988      */
18989     addInvalidHandleClass: function(cssClass) {
18990         this.invalidHandleClasses.push(cssClass);
18991     },
18992
18993     /**
18994      * Unsets an excluded tag name set by addInvalidHandleType
18995      * @method removeInvalidHandleType
18996      * @param {string} tagName the type of element to unexclude
18997      */
18998     removeInvalidHandleType: function(tagName) {
18999         var type = tagName.toUpperCase();
19000         // this.invalidHandleTypes[type] = null;
19001         delete this.invalidHandleTypes[type];
19002     },
19003
19004     /**
19005      * Unsets an invalid handle id
19006      * @method removeInvalidHandleId
19007      * @param {string} id the id of the element to re-enable
19008      */
19009     removeInvalidHandleId: function(id) {
19010         if (typeof id !== "string") {
19011             id = Roo.id(id);
19012         }
19013         delete this.invalidHandleIds[id];
19014     },
19015
19016     /**
19017      * Unsets an invalid css class
19018      * @method removeInvalidHandleClass
19019      * @param {string} cssClass the class of the element(s) you wish to
19020      * re-enable
19021      */
19022     removeInvalidHandleClass: function(cssClass) {
19023         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19024             if (this.invalidHandleClasses[i] == cssClass) {
19025                 delete this.invalidHandleClasses[i];
19026             }
19027         }
19028     },
19029
19030     /**
19031      * Checks the tag exclusion list to see if this click should be ignored
19032      * @method isValidHandleChild
19033      * @param {HTMLElement} node the HTMLElement to evaluate
19034      * @return {boolean} true if this is a valid tag type, false if not
19035      */
19036     isValidHandleChild: function(node) {
19037
19038         var valid = true;
19039         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19040         var nodeName;
19041         try {
19042             nodeName = node.nodeName.toUpperCase();
19043         } catch(e) {
19044             nodeName = node.nodeName;
19045         }
19046         valid = valid && !this.invalidHandleTypes[nodeName];
19047         valid = valid && !this.invalidHandleIds[node.id];
19048
19049         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19050             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19051         }
19052
19053
19054         return valid;
19055
19056     },
19057
19058     /**
19059      * Create the array of horizontal tick marks if an interval was specified
19060      * in setXConstraint().
19061      * @method setXTicks
19062      * @private
19063      */
19064     setXTicks: function(iStartX, iTickSize) {
19065         this.xTicks = [];
19066         this.xTickSize = iTickSize;
19067
19068         var tickMap = {};
19069
19070         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19071             if (!tickMap[i]) {
19072                 this.xTicks[this.xTicks.length] = i;
19073                 tickMap[i] = true;
19074             }
19075         }
19076
19077         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19078             if (!tickMap[i]) {
19079                 this.xTicks[this.xTicks.length] = i;
19080                 tickMap[i] = true;
19081             }
19082         }
19083
19084         this.xTicks.sort(this.DDM.numericSort) ;
19085     },
19086
19087     /**
19088      * Create the array of vertical tick marks if an interval was specified in
19089      * setYConstraint().
19090      * @method setYTicks
19091      * @private
19092      */
19093     setYTicks: function(iStartY, iTickSize) {
19094         this.yTicks = [];
19095         this.yTickSize = iTickSize;
19096
19097         var tickMap = {};
19098
19099         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19100             if (!tickMap[i]) {
19101                 this.yTicks[this.yTicks.length] = i;
19102                 tickMap[i] = true;
19103             }
19104         }
19105
19106         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19107             if (!tickMap[i]) {
19108                 this.yTicks[this.yTicks.length] = i;
19109                 tickMap[i] = true;
19110             }
19111         }
19112
19113         this.yTicks.sort(this.DDM.numericSort) ;
19114     },
19115
19116     /**
19117      * By default, the element can be dragged any place on the screen.  Use
19118      * this method to limit the horizontal travel of the element.  Pass in
19119      * 0,0 for the parameters if you want to lock the drag to the y axis.
19120      * @method setXConstraint
19121      * @param {int} iLeft the number of pixels the element can move to the left
19122      * @param {int} iRight the number of pixels the element can move to the
19123      * right
19124      * @param {int} iTickSize optional parameter for specifying that the
19125      * element
19126      * should move iTickSize pixels at a time.
19127      */
19128     setXConstraint: function(iLeft, iRight, iTickSize) {
19129         this.leftConstraint = iLeft;
19130         this.rightConstraint = iRight;
19131
19132         this.minX = this.initPageX - iLeft;
19133         this.maxX = this.initPageX + iRight;
19134         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19135
19136         this.constrainX = true;
19137     },
19138
19139     /**
19140      * Clears any constraints applied to this instance.  Also clears ticks
19141      * since they can't exist independent of a constraint at this time.
19142      * @method clearConstraints
19143      */
19144     clearConstraints: function() {
19145         this.constrainX = false;
19146         this.constrainY = false;
19147         this.clearTicks();
19148     },
19149
19150     /**
19151      * Clears any tick interval defined for this instance
19152      * @method clearTicks
19153      */
19154     clearTicks: function() {
19155         this.xTicks = null;
19156         this.yTicks = null;
19157         this.xTickSize = 0;
19158         this.yTickSize = 0;
19159     },
19160
19161     /**
19162      * By default, the element can be dragged any place on the screen.  Set
19163      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19164      * parameters if you want to lock the drag to the x axis.
19165      * @method setYConstraint
19166      * @param {int} iUp the number of pixels the element can move up
19167      * @param {int} iDown the number of pixels the element can move down
19168      * @param {int} iTickSize optional parameter for specifying that the
19169      * element should move iTickSize pixels at a time.
19170      */
19171     setYConstraint: function(iUp, iDown, iTickSize) {
19172         this.topConstraint = iUp;
19173         this.bottomConstraint = iDown;
19174
19175         this.minY = this.initPageY - iUp;
19176         this.maxY = this.initPageY + iDown;
19177         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19178
19179         this.constrainY = true;
19180
19181     },
19182
19183     /**
19184      * resetConstraints must be called if you manually reposition a dd element.
19185      * @method resetConstraints
19186      * @param {boolean} maintainOffset
19187      */
19188     resetConstraints: function() {
19189
19190
19191         // Maintain offsets if necessary
19192         if (this.initPageX || this.initPageX === 0) {
19193             // figure out how much this thing has moved
19194             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19195             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19196
19197             this.setInitPosition(dx, dy);
19198
19199         // This is the first time we have detected the element's position
19200         } else {
19201             this.setInitPosition();
19202         }
19203
19204         if (this.constrainX) {
19205             this.setXConstraint( this.leftConstraint,
19206                                  this.rightConstraint,
19207                                  this.xTickSize        );
19208         }
19209
19210         if (this.constrainY) {
19211             this.setYConstraint( this.topConstraint,
19212                                  this.bottomConstraint,
19213                                  this.yTickSize         );
19214         }
19215     },
19216
19217     /**
19218      * Normally the drag element is moved pixel by pixel, but we can specify
19219      * that it move a number of pixels at a time.  This method resolves the
19220      * location when we have it set up like this.
19221      * @method getTick
19222      * @param {int} val where we want to place the object
19223      * @param {int[]} tickArray sorted array of valid points
19224      * @return {int} the closest tick
19225      * @private
19226      */
19227     getTick: function(val, tickArray) {
19228
19229         if (!tickArray) {
19230             // If tick interval is not defined, it is effectively 1 pixel,
19231             // so we return the value passed to us.
19232             return val;
19233         } else if (tickArray[0] >= val) {
19234             // The value is lower than the first tick, so we return the first
19235             // tick.
19236             return tickArray[0];
19237         } else {
19238             for (var i=0, len=tickArray.length; i<len; ++i) {
19239                 var next = i + 1;
19240                 if (tickArray[next] && tickArray[next] >= val) {
19241                     var diff1 = val - tickArray[i];
19242                     var diff2 = tickArray[next] - val;
19243                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19244                 }
19245             }
19246
19247             // The value is larger than the last tick, so we return the last
19248             // tick.
19249             return tickArray[tickArray.length - 1];
19250         }
19251     },
19252
19253     /**
19254      * toString method
19255      * @method toString
19256      * @return {string} string representation of the dd obj
19257      */
19258     toString: function() {
19259         return ("DragDrop " + this.id);
19260     }
19261
19262 });
19263
19264 })();
19265 /*
19266  * Based on:
19267  * Ext JS Library 1.1.1
19268  * Copyright(c) 2006-2007, Ext JS, LLC.
19269  *
19270  * Originally Released Under LGPL - original licence link has changed is not relivant.
19271  *
19272  * Fork - LGPL
19273  * <script type="text/javascript">
19274  */
19275
19276
19277 /**
19278  * The drag and drop utility provides a framework for building drag and drop
19279  * applications.  In addition to enabling drag and drop for specific elements,
19280  * the drag and drop elements are tracked by the manager class, and the
19281  * interactions between the various elements are tracked during the drag and
19282  * the implementing code is notified about these important moments.
19283  */
19284
19285 // Only load the library once.  Rewriting the manager class would orphan
19286 // existing drag and drop instances.
19287 if (!Roo.dd.DragDropMgr) {
19288
19289 /**
19290  * @class Roo.dd.DragDropMgr
19291  * DragDropMgr is a singleton that tracks the element interaction for
19292  * all DragDrop items in the window.  Generally, you will not call
19293  * this class directly, but it does have helper methods that could
19294  * be useful in your DragDrop implementations.
19295  * @singleton
19296  */
19297 Roo.dd.DragDropMgr = function() {
19298
19299     var Event = Roo.EventManager;
19300
19301     return {
19302
19303         /**
19304          * Two dimensional Array of registered DragDrop objects.  The first
19305          * dimension is the DragDrop item group, the second the DragDrop
19306          * object.
19307          * @property ids
19308          * @type {string: string}
19309          * @private
19310          * @static
19311          */
19312         ids: {},
19313
19314         /**
19315          * Array of element ids defined as drag handles.  Used to determine
19316          * if the element that generated the mousedown event is actually the
19317          * handle and not the html element itself.
19318          * @property handleIds
19319          * @type {string: string}
19320          * @private
19321          * @static
19322          */
19323         handleIds: {},
19324
19325         /**
19326          * the DragDrop object that is currently being dragged
19327          * @property dragCurrent
19328          * @type DragDrop
19329          * @private
19330          * @static
19331          **/
19332         dragCurrent: null,
19333
19334         /**
19335          * the DragDrop object(s) that are being hovered over
19336          * @property dragOvers
19337          * @type Array
19338          * @private
19339          * @static
19340          */
19341         dragOvers: {},
19342
19343         /**
19344          * the X distance between the cursor and the object being dragged
19345          * @property deltaX
19346          * @type int
19347          * @private
19348          * @static
19349          */
19350         deltaX: 0,
19351
19352         /**
19353          * the Y distance between the cursor and the object being dragged
19354          * @property deltaY
19355          * @type int
19356          * @private
19357          * @static
19358          */
19359         deltaY: 0,
19360
19361         /**
19362          * Flag to determine if we should prevent the default behavior of the
19363          * events we define. By default this is true, but this can be set to
19364          * false if you need the default behavior (not recommended)
19365          * @property preventDefault
19366          * @type boolean
19367          * @static
19368          */
19369         preventDefault: true,
19370
19371         /**
19372          * Flag to determine if we should stop the propagation of the events
19373          * we generate. This is true by default but you may want to set it to
19374          * false if the html element contains other features that require the
19375          * mouse click.
19376          * @property stopPropagation
19377          * @type boolean
19378          * @static
19379          */
19380         stopPropagation: true,
19381
19382         /**
19383          * Internal flag that is set to true when drag and drop has been
19384          * intialized
19385          * @property initialized
19386          * @private
19387          * @static
19388          */
19389         initalized: false,
19390
19391         /**
19392          * All drag and drop can be disabled.
19393          * @property locked
19394          * @private
19395          * @static
19396          */
19397         locked: false,
19398
19399         /**
19400          * Called the first time an element is registered.
19401          * @method init
19402          * @private
19403          * @static
19404          */
19405         init: function() {
19406             this.initialized = true;
19407         },
19408
19409         /**
19410          * In point mode, drag and drop interaction is defined by the
19411          * location of the cursor during the drag/drop
19412          * @property POINT
19413          * @type int
19414          * @static
19415          */
19416         POINT: 0,
19417
19418         /**
19419          * In intersect mode, drag and drop interactio nis defined by the
19420          * overlap of two or more drag and drop objects.
19421          * @property INTERSECT
19422          * @type int
19423          * @static
19424          */
19425         INTERSECT: 1,
19426
19427         /**
19428          * The current drag and drop mode.  Default: POINT
19429          * @property mode
19430          * @type int
19431          * @static
19432          */
19433         mode: 0,
19434
19435         /**
19436          * Runs method on all drag and drop objects
19437          * @method _execOnAll
19438          * @private
19439          * @static
19440          */
19441         _execOnAll: function(sMethod, args) {
19442             for (var i in this.ids) {
19443                 for (var j in this.ids[i]) {
19444                     var oDD = this.ids[i][j];
19445                     if (! this.isTypeOfDD(oDD)) {
19446                         continue;
19447                     }
19448                     oDD[sMethod].apply(oDD, args);
19449                 }
19450             }
19451         },
19452
19453         /**
19454          * Drag and drop initialization.  Sets up the global event handlers
19455          * @method _onLoad
19456          * @private
19457          * @static
19458          */
19459         _onLoad: function() {
19460
19461             this.init();
19462
19463             if (!Roo.isTouch) {
19464                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
19465                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
19466             }
19467             Event.on(document, "touchend",   this.handleMouseUp, this, true);
19468             Event.on(document, "touchmove", this.handleMouseMove, this, true);
19469             
19470             Event.on(window,   "unload",    this._onUnload, this, true);
19471             Event.on(window,   "resize",    this._onResize, this, true);
19472             // Event.on(window,   "mouseout",    this._test);
19473
19474         },
19475
19476         /**
19477          * Reset constraints on all drag and drop objs
19478          * @method _onResize
19479          * @private
19480          * @static
19481          */
19482         _onResize: function(e) {
19483             this._execOnAll("resetConstraints", []);
19484         },
19485
19486         /**
19487          * Lock all drag and drop functionality
19488          * @method lock
19489          * @static
19490          */
19491         lock: function() { this.locked = true; },
19492
19493         /**
19494          * Unlock all drag and drop functionality
19495          * @method unlock
19496          * @static
19497          */
19498         unlock: function() { this.locked = false; },
19499
19500         /**
19501          * Is drag and drop locked?
19502          * @method isLocked
19503          * @return {boolean} True if drag and drop is locked, false otherwise.
19504          * @static
19505          */
19506         isLocked: function() { return this.locked; },
19507
19508         /**
19509          * Location cache that is set for all drag drop objects when a drag is
19510          * initiated, cleared when the drag is finished.
19511          * @property locationCache
19512          * @private
19513          * @static
19514          */
19515         locationCache: {},
19516
19517         /**
19518          * Set useCache to false if you want to force object the lookup of each
19519          * drag and drop linked element constantly during a drag.
19520          * @property useCache
19521          * @type boolean
19522          * @static
19523          */
19524         useCache: true,
19525
19526         /**
19527          * The number of pixels that the mouse needs to move after the
19528          * mousedown before the drag is initiated.  Default=3;
19529          * @property clickPixelThresh
19530          * @type int
19531          * @static
19532          */
19533         clickPixelThresh: 3,
19534
19535         /**
19536          * The number of milliseconds after the mousedown event to initiate the
19537          * drag if we don't get a mouseup event. Default=1000
19538          * @property clickTimeThresh
19539          * @type int
19540          * @static
19541          */
19542         clickTimeThresh: 350,
19543
19544         /**
19545          * Flag that indicates that either the drag pixel threshold or the
19546          * mousdown time threshold has been met
19547          * @property dragThreshMet
19548          * @type boolean
19549          * @private
19550          * @static
19551          */
19552         dragThreshMet: false,
19553
19554         /**
19555          * Timeout used for the click time threshold
19556          * @property clickTimeout
19557          * @type Object
19558          * @private
19559          * @static
19560          */
19561         clickTimeout: null,
19562
19563         /**
19564          * The X position of the mousedown event stored for later use when a
19565          * drag threshold is met.
19566          * @property startX
19567          * @type int
19568          * @private
19569          * @static
19570          */
19571         startX: 0,
19572
19573         /**
19574          * The Y position of the mousedown event stored for later use when a
19575          * drag threshold is met.
19576          * @property startY
19577          * @type int
19578          * @private
19579          * @static
19580          */
19581         startY: 0,
19582
19583         /**
19584          * Each DragDrop instance must be registered with the DragDropMgr.
19585          * This is executed in DragDrop.init()
19586          * @method regDragDrop
19587          * @param {DragDrop} oDD the DragDrop object to register
19588          * @param {String} sGroup the name of the group this element belongs to
19589          * @static
19590          */
19591         regDragDrop: function(oDD, sGroup) {
19592             if (!this.initialized) { this.init(); }
19593
19594             if (!this.ids[sGroup]) {
19595                 this.ids[sGroup] = {};
19596             }
19597             this.ids[sGroup][oDD.id] = oDD;
19598         },
19599
19600         /**
19601          * Removes the supplied dd instance from the supplied group. Executed
19602          * by DragDrop.removeFromGroup, so don't call this function directly.
19603          * @method removeDDFromGroup
19604          * @private
19605          * @static
19606          */
19607         removeDDFromGroup: function(oDD, sGroup) {
19608             if (!this.ids[sGroup]) {
19609                 this.ids[sGroup] = {};
19610             }
19611
19612             var obj = this.ids[sGroup];
19613             if (obj && obj[oDD.id]) {
19614                 delete obj[oDD.id];
19615             }
19616         },
19617
19618         /**
19619          * Unregisters a drag and drop item.  This is executed in
19620          * DragDrop.unreg, use that method instead of calling this directly.
19621          * @method _remove
19622          * @private
19623          * @static
19624          */
19625         _remove: function(oDD) {
19626             for (var g in oDD.groups) {
19627                 if (g && this.ids[g][oDD.id]) {
19628                     delete this.ids[g][oDD.id];
19629                 }
19630             }
19631             delete this.handleIds[oDD.id];
19632         },
19633
19634         /**
19635          * Each DragDrop handle element must be registered.  This is done
19636          * automatically when executing DragDrop.setHandleElId()
19637          * @method regHandle
19638          * @param {String} sDDId the DragDrop id this element is a handle for
19639          * @param {String} sHandleId the id of the element that is the drag
19640          * handle
19641          * @static
19642          */
19643         regHandle: function(sDDId, sHandleId) {
19644             if (!this.handleIds[sDDId]) {
19645                 this.handleIds[sDDId] = {};
19646             }
19647             this.handleIds[sDDId][sHandleId] = sHandleId;
19648         },
19649
19650         /**
19651          * Utility function to determine if a given element has been
19652          * registered as a drag drop item.
19653          * @method isDragDrop
19654          * @param {String} id the element id to check
19655          * @return {boolean} true if this element is a DragDrop item,
19656          * false otherwise
19657          * @static
19658          */
19659         isDragDrop: function(id) {
19660             return ( this.getDDById(id) ) ? true : false;
19661         },
19662
19663         /**
19664          * Returns the drag and drop instances that are in all groups the
19665          * passed in instance belongs to.
19666          * @method getRelated
19667          * @param {DragDrop} p_oDD the obj to get related data for
19668          * @param {boolean} bTargetsOnly if true, only return targetable objs
19669          * @return {DragDrop[]} the related instances
19670          * @static
19671          */
19672         getRelated: function(p_oDD, bTargetsOnly) {
19673             var oDDs = [];
19674             for (var i in p_oDD.groups) {
19675                 for (j in this.ids[i]) {
19676                     var dd = this.ids[i][j];
19677                     if (! this.isTypeOfDD(dd)) {
19678                         continue;
19679                     }
19680                     if (!bTargetsOnly || dd.isTarget) {
19681                         oDDs[oDDs.length] = dd;
19682                     }
19683                 }
19684             }
19685
19686             return oDDs;
19687         },
19688
19689         /**
19690          * Returns true if the specified dd target is a legal target for
19691          * the specifice drag obj
19692          * @method isLegalTarget
19693          * @param {DragDrop} the drag obj
19694          * @param {DragDrop} the target
19695          * @return {boolean} true if the target is a legal target for the
19696          * dd obj
19697          * @static
19698          */
19699         isLegalTarget: function (oDD, oTargetDD) {
19700             var targets = this.getRelated(oDD, true);
19701             for (var i=0, len=targets.length;i<len;++i) {
19702                 if (targets[i].id == oTargetDD.id) {
19703                     return true;
19704                 }
19705             }
19706
19707             return false;
19708         },
19709
19710         /**
19711          * My goal is to be able to transparently determine if an object is
19712          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
19713          * returns "object", oDD.constructor.toString() always returns
19714          * "DragDrop" and not the name of the subclass.  So for now it just
19715          * evaluates a well-known variable in DragDrop.
19716          * @method isTypeOfDD
19717          * @param {Object} the object to evaluate
19718          * @return {boolean} true if typeof oDD = DragDrop
19719          * @static
19720          */
19721         isTypeOfDD: function (oDD) {
19722             return (oDD && oDD.__ygDragDrop);
19723         },
19724
19725         /**
19726          * Utility function to determine if a given element has been
19727          * registered as a drag drop handle for the given Drag Drop object.
19728          * @method isHandle
19729          * @param {String} id the element id to check
19730          * @return {boolean} true if this element is a DragDrop handle, false
19731          * otherwise
19732          * @static
19733          */
19734         isHandle: function(sDDId, sHandleId) {
19735             return ( this.handleIds[sDDId] &&
19736                             this.handleIds[sDDId][sHandleId] );
19737         },
19738
19739         /**
19740          * Returns the DragDrop instance for a given id
19741          * @method getDDById
19742          * @param {String} id the id of the DragDrop object
19743          * @return {DragDrop} the drag drop object, null if it is not found
19744          * @static
19745          */
19746         getDDById: function(id) {
19747             for (var i in this.ids) {
19748                 if (this.ids[i][id]) {
19749                     return this.ids[i][id];
19750                 }
19751             }
19752             return null;
19753         },
19754
19755         /**
19756          * Fired after a registered DragDrop object gets the mousedown event.
19757          * Sets up the events required to track the object being dragged
19758          * @method handleMouseDown
19759          * @param {Event} e the event
19760          * @param oDD the DragDrop object being dragged
19761          * @private
19762          * @static
19763          */
19764         handleMouseDown: function(e, oDD) {
19765             if(Roo.QuickTips){
19766                 Roo.QuickTips.disable();
19767             }
19768             this.currentTarget = e.getTarget();
19769
19770             this.dragCurrent = oDD;
19771
19772             var el = oDD.getEl();
19773
19774             // track start position
19775             this.startX = e.getPageX();
19776             this.startY = e.getPageY();
19777
19778             this.deltaX = this.startX - el.offsetLeft;
19779             this.deltaY = this.startY - el.offsetTop;
19780
19781             this.dragThreshMet = false;
19782
19783             this.clickTimeout = setTimeout(
19784                     function() {
19785                         var DDM = Roo.dd.DDM;
19786                         DDM.startDrag(DDM.startX, DDM.startY);
19787                     },
19788                     this.clickTimeThresh );
19789         },
19790
19791         /**
19792          * Fired when either the drag pixel threshol or the mousedown hold
19793          * time threshold has been met.
19794          * @method startDrag
19795          * @param x {int} the X position of the original mousedown
19796          * @param y {int} the Y position of the original mousedown
19797          * @static
19798          */
19799         startDrag: function(x, y) {
19800             clearTimeout(this.clickTimeout);
19801             if (this.dragCurrent) {
19802                 this.dragCurrent.b4StartDrag(x, y);
19803                 this.dragCurrent.startDrag(x, y);
19804             }
19805             this.dragThreshMet = true;
19806         },
19807
19808         /**
19809          * Internal function to handle the mouseup event.  Will be invoked
19810          * from the context of the document.
19811          * @method handleMouseUp
19812          * @param {Event} e the event
19813          * @private
19814          * @static
19815          */
19816         handleMouseUp: function(e) {
19817
19818             if(Roo.QuickTips){
19819                 Roo.QuickTips.enable();
19820             }
19821             if (! this.dragCurrent) {
19822                 return;
19823             }
19824
19825             clearTimeout(this.clickTimeout);
19826
19827             if (this.dragThreshMet) {
19828                 this.fireEvents(e, true);
19829             } else {
19830             }
19831
19832             this.stopDrag(e);
19833
19834             this.stopEvent(e);
19835         },
19836
19837         /**
19838          * Utility to stop event propagation and event default, if these
19839          * features are turned on.
19840          * @method stopEvent
19841          * @param {Event} e the event as returned by this.getEvent()
19842          * @static
19843          */
19844         stopEvent: function(e){
19845             if(this.stopPropagation) {
19846                 e.stopPropagation();
19847             }
19848
19849             if (this.preventDefault) {
19850                 e.preventDefault();
19851             }
19852         },
19853
19854         /**
19855          * Internal function to clean up event handlers after the drag
19856          * operation is complete
19857          * @method stopDrag
19858          * @param {Event} e the event
19859          * @private
19860          * @static
19861          */
19862         stopDrag: function(e) {
19863             // Fire the drag end event for the item that was dragged
19864             if (this.dragCurrent) {
19865                 if (this.dragThreshMet) {
19866                     this.dragCurrent.b4EndDrag(e);
19867                     this.dragCurrent.endDrag(e);
19868                 }
19869
19870                 this.dragCurrent.onMouseUp(e);
19871             }
19872
19873             this.dragCurrent = null;
19874             this.dragOvers = {};
19875         },
19876
19877         /**
19878          * Internal function to handle the mousemove event.  Will be invoked
19879          * from the context of the html element.
19880          *
19881          * @TODO figure out what we can do about mouse events lost when the
19882          * user drags objects beyond the window boundary.  Currently we can
19883          * detect this in internet explorer by verifying that the mouse is
19884          * down during the mousemove event.  Firefox doesn't give us the
19885          * button state on the mousemove event.
19886          * @method handleMouseMove
19887          * @param {Event} e the event
19888          * @private
19889          * @static
19890          */
19891         handleMouseMove: function(e) {
19892             if (! this.dragCurrent) {
19893                 return true;
19894             }
19895
19896             // var button = e.which || e.button;
19897
19898             // check for IE mouseup outside of page boundary
19899             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
19900                 this.stopEvent(e);
19901                 return this.handleMouseUp(e);
19902             }
19903
19904             if (!this.dragThreshMet) {
19905                 var diffX = Math.abs(this.startX - e.getPageX());
19906                 var diffY = Math.abs(this.startY - e.getPageY());
19907                 if (diffX > this.clickPixelThresh ||
19908                             diffY > this.clickPixelThresh) {
19909                     this.startDrag(this.startX, this.startY);
19910                 }
19911             }
19912
19913             if (this.dragThreshMet) {
19914                 this.dragCurrent.b4Drag(e);
19915                 this.dragCurrent.onDrag(e);
19916                 if(!this.dragCurrent.moveOnly){
19917                     this.fireEvents(e, false);
19918                 }
19919             }
19920
19921             this.stopEvent(e);
19922
19923             return true;
19924         },
19925
19926         /**
19927          * Iterates over all of the DragDrop elements to find ones we are
19928          * hovering over or dropping on
19929          * @method fireEvents
19930          * @param {Event} e the event
19931          * @param {boolean} isDrop is this a drop op or a mouseover op?
19932          * @private
19933          * @static
19934          */
19935         fireEvents: function(e, isDrop) {
19936             var dc = this.dragCurrent;
19937
19938             // If the user did the mouse up outside of the window, we could
19939             // get here even though we have ended the drag.
19940             if (!dc || dc.isLocked()) {
19941                 return;
19942             }
19943
19944             var pt = e.getPoint();
19945
19946             // cache the previous dragOver array
19947             var oldOvers = [];
19948
19949             var outEvts   = [];
19950             var overEvts  = [];
19951             var dropEvts  = [];
19952             var enterEvts = [];
19953
19954             // Check to see if the object(s) we were hovering over is no longer
19955             // being hovered over so we can fire the onDragOut event
19956             for (var i in this.dragOvers) {
19957
19958                 var ddo = this.dragOvers[i];
19959
19960                 if (! this.isTypeOfDD(ddo)) {
19961                     continue;
19962                 }
19963
19964                 if (! this.isOverTarget(pt, ddo, this.mode)) {
19965                     outEvts.push( ddo );
19966                 }
19967
19968                 oldOvers[i] = true;
19969                 delete this.dragOvers[i];
19970             }
19971
19972             for (var sGroup in dc.groups) {
19973
19974                 if ("string" != typeof sGroup) {
19975                     continue;
19976                 }
19977
19978                 for (i in this.ids[sGroup]) {
19979                     var oDD = this.ids[sGroup][i];
19980                     if (! this.isTypeOfDD(oDD)) {
19981                         continue;
19982                     }
19983
19984                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
19985                         if (this.isOverTarget(pt, oDD, this.mode)) {
19986                             // look for drop interactions
19987                             if (isDrop) {
19988                                 dropEvts.push( oDD );
19989                             // look for drag enter and drag over interactions
19990                             } else {
19991
19992                                 // initial drag over: dragEnter fires
19993                                 if (!oldOvers[oDD.id]) {
19994                                     enterEvts.push( oDD );
19995                                 // subsequent drag overs: dragOver fires
19996                                 } else {
19997                                     overEvts.push( oDD );
19998                                 }
19999
20000                                 this.dragOvers[oDD.id] = oDD;
20001                             }
20002                         }
20003                     }
20004                 }
20005             }
20006
20007             if (this.mode) {
20008                 if (outEvts.length) {
20009                     dc.b4DragOut(e, outEvts);
20010                     dc.onDragOut(e, outEvts);
20011                 }
20012
20013                 if (enterEvts.length) {
20014                     dc.onDragEnter(e, enterEvts);
20015                 }
20016
20017                 if (overEvts.length) {
20018                     dc.b4DragOver(e, overEvts);
20019                     dc.onDragOver(e, overEvts);
20020                 }
20021
20022                 if (dropEvts.length) {
20023                     dc.b4DragDrop(e, dropEvts);
20024                     dc.onDragDrop(e, dropEvts);
20025                 }
20026
20027             } else {
20028                 // fire dragout events
20029                 var len = 0;
20030                 for (i=0, len=outEvts.length; i<len; ++i) {
20031                     dc.b4DragOut(e, outEvts[i].id);
20032                     dc.onDragOut(e, outEvts[i].id);
20033                 }
20034
20035                 // fire enter events
20036                 for (i=0,len=enterEvts.length; i<len; ++i) {
20037                     // dc.b4DragEnter(e, oDD.id);
20038                     dc.onDragEnter(e, enterEvts[i].id);
20039                 }
20040
20041                 // fire over events
20042                 for (i=0,len=overEvts.length; i<len; ++i) {
20043                     dc.b4DragOver(e, overEvts[i].id);
20044                     dc.onDragOver(e, overEvts[i].id);
20045                 }
20046
20047                 // fire drop events
20048                 for (i=0, len=dropEvts.length; i<len; ++i) {
20049                     dc.b4DragDrop(e, dropEvts[i].id);
20050                     dc.onDragDrop(e, dropEvts[i].id);
20051                 }
20052
20053             }
20054
20055             // notify about a drop that did not find a target
20056             if (isDrop && !dropEvts.length) {
20057                 dc.onInvalidDrop(e);
20058             }
20059
20060         },
20061
20062         /**
20063          * Helper function for getting the best match from the list of drag
20064          * and drop objects returned by the drag and drop events when we are
20065          * in INTERSECT mode.  It returns either the first object that the
20066          * cursor is over, or the object that has the greatest overlap with
20067          * the dragged element.
20068          * @method getBestMatch
20069          * @param  {DragDrop[]} dds The array of drag and drop objects
20070          * targeted
20071          * @return {DragDrop}       The best single match
20072          * @static
20073          */
20074         getBestMatch: function(dds) {
20075             var winner = null;
20076             // Return null if the input is not what we expect
20077             //if (!dds || !dds.length || dds.length == 0) {
20078                // winner = null;
20079             // If there is only one item, it wins
20080             //} else if (dds.length == 1) {
20081
20082             var len = dds.length;
20083
20084             if (len == 1) {
20085                 winner = dds[0];
20086             } else {
20087                 // Loop through the targeted items
20088                 for (var i=0; i<len; ++i) {
20089                     var dd = dds[i];
20090                     // If the cursor is over the object, it wins.  If the
20091                     // cursor is over multiple matches, the first one we come
20092                     // to wins.
20093                     if (dd.cursorIsOver) {
20094                         winner = dd;
20095                         break;
20096                     // Otherwise the object with the most overlap wins
20097                     } else {
20098                         if (!winner ||
20099                             winner.overlap.getArea() < dd.overlap.getArea()) {
20100                             winner = dd;
20101                         }
20102                     }
20103                 }
20104             }
20105
20106             return winner;
20107         },
20108
20109         /**
20110          * Refreshes the cache of the top-left and bottom-right points of the
20111          * drag and drop objects in the specified group(s).  This is in the
20112          * format that is stored in the drag and drop instance, so typical
20113          * usage is:
20114          * <code>
20115          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20116          * </code>
20117          * Alternatively:
20118          * <code>
20119          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20120          * </code>
20121          * @TODO this really should be an indexed array.  Alternatively this
20122          * method could accept both.
20123          * @method refreshCache
20124          * @param {Object} groups an associative array of groups to refresh
20125          * @static
20126          */
20127         refreshCache: function(groups) {
20128             for (var sGroup in groups) {
20129                 if ("string" != typeof sGroup) {
20130                     continue;
20131                 }
20132                 for (var i in this.ids[sGroup]) {
20133                     var oDD = this.ids[sGroup][i];
20134
20135                     if (this.isTypeOfDD(oDD)) {
20136                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20137                         var loc = this.getLocation(oDD);
20138                         if (loc) {
20139                             this.locationCache[oDD.id] = loc;
20140                         } else {
20141                             delete this.locationCache[oDD.id];
20142                             // this will unregister the drag and drop object if
20143                             // the element is not in a usable state
20144                             // oDD.unreg();
20145                         }
20146                     }
20147                 }
20148             }
20149         },
20150
20151         /**
20152          * This checks to make sure an element exists and is in the DOM.  The
20153          * main purpose is to handle cases where innerHTML is used to remove
20154          * drag and drop objects from the DOM.  IE provides an 'unspecified
20155          * error' when trying to access the offsetParent of such an element
20156          * @method verifyEl
20157          * @param {HTMLElement} el the element to check
20158          * @return {boolean} true if the element looks usable
20159          * @static
20160          */
20161         verifyEl: function(el) {
20162             if (el) {
20163                 var parent;
20164                 if(Roo.isIE){
20165                     try{
20166                         parent = el.offsetParent;
20167                     }catch(e){}
20168                 }else{
20169                     parent = el.offsetParent;
20170                 }
20171                 if (parent) {
20172                     return true;
20173                 }
20174             }
20175
20176             return false;
20177         },
20178
20179         /**
20180          * Returns a Region object containing the drag and drop element's position
20181          * and size, including the padding configured for it
20182          * @method getLocation
20183          * @param {DragDrop} oDD the drag and drop object to get the
20184          *                       location for
20185          * @return {Roo.lib.Region} a Region object representing the total area
20186          *                             the element occupies, including any padding
20187          *                             the instance is configured for.
20188          * @static
20189          */
20190         getLocation: function(oDD) {
20191             if (! this.isTypeOfDD(oDD)) {
20192                 return null;
20193             }
20194
20195             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20196
20197             try {
20198                 pos= Roo.lib.Dom.getXY(el);
20199             } catch (e) { }
20200
20201             if (!pos) {
20202                 return null;
20203             }
20204
20205             x1 = pos[0];
20206             x2 = x1 + el.offsetWidth;
20207             y1 = pos[1];
20208             y2 = y1 + el.offsetHeight;
20209
20210             t = y1 - oDD.padding[0];
20211             r = x2 + oDD.padding[1];
20212             b = y2 + oDD.padding[2];
20213             l = x1 - oDD.padding[3];
20214
20215             return new Roo.lib.Region( t, r, b, l );
20216         },
20217
20218         /**
20219          * Checks the cursor location to see if it over the target
20220          * @method isOverTarget
20221          * @param {Roo.lib.Point} pt The point to evaluate
20222          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20223          * @return {boolean} true if the mouse is over the target
20224          * @private
20225          * @static
20226          */
20227         isOverTarget: function(pt, oTarget, intersect) {
20228             // use cache if available
20229             var loc = this.locationCache[oTarget.id];
20230             if (!loc || !this.useCache) {
20231                 loc = this.getLocation(oTarget);
20232                 this.locationCache[oTarget.id] = loc;
20233
20234             }
20235
20236             if (!loc) {
20237                 return false;
20238             }
20239
20240             oTarget.cursorIsOver = loc.contains( pt );
20241
20242             // DragDrop is using this as a sanity check for the initial mousedown
20243             // in this case we are done.  In POINT mode, if the drag obj has no
20244             // contraints, we are also done. Otherwise we need to evaluate the
20245             // location of the target as related to the actual location of the
20246             // dragged element.
20247             var dc = this.dragCurrent;
20248             if (!dc || !dc.getTargetCoord ||
20249                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20250                 return oTarget.cursorIsOver;
20251             }
20252
20253             oTarget.overlap = null;
20254
20255             // Get the current location of the drag element, this is the
20256             // location of the mouse event less the delta that represents
20257             // where the original mousedown happened on the element.  We
20258             // need to consider constraints and ticks as well.
20259             var pos = dc.getTargetCoord(pt.x, pt.y);
20260
20261             var el = dc.getDragEl();
20262             var curRegion = new Roo.lib.Region( pos.y,
20263                                                    pos.x + el.offsetWidth,
20264                                                    pos.y + el.offsetHeight,
20265                                                    pos.x );
20266
20267             var overlap = curRegion.intersect(loc);
20268
20269             if (overlap) {
20270                 oTarget.overlap = overlap;
20271                 return (intersect) ? true : oTarget.cursorIsOver;
20272             } else {
20273                 return false;
20274             }
20275         },
20276
20277         /**
20278          * unload event handler
20279          * @method _onUnload
20280          * @private
20281          * @static
20282          */
20283         _onUnload: function(e, me) {
20284             Roo.dd.DragDropMgr.unregAll();
20285         },
20286
20287         /**
20288          * Cleans up the drag and drop events and objects.
20289          * @method unregAll
20290          * @private
20291          * @static
20292          */
20293         unregAll: function() {
20294
20295             if (this.dragCurrent) {
20296                 this.stopDrag();
20297                 this.dragCurrent = null;
20298             }
20299
20300             this._execOnAll("unreg", []);
20301
20302             for (i in this.elementCache) {
20303                 delete this.elementCache[i];
20304             }
20305
20306             this.elementCache = {};
20307             this.ids = {};
20308         },
20309
20310         /**
20311          * A cache of DOM elements
20312          * @property elementCache
20313          * @private
20314          * @static
20315          */
20316         elementCache: {},
20317
20318         /**
20319          * Get the wrapper for the DOM element specified
20320          * @method getElWrapper
20321          * @param {String} id the id of the element to get
20322          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20323          * @private
20324          * @deprecated This wrapper isn't that useful
20325          * @static
20326          */
20327         getElWrapper: function(id) {
20328             var oWrapper = this.elementCache[id];
20329             if (!oWrapper || !oWrapper.el) {
20330                 oWrapper = this.elementCache[id] =
20331                     new this.ElementWrapper(Roo.getDom(id));
20332             }
20333             return oWrapper;
20334         },
20335
20336         /**
20337          * Returns the actual DOM element
20338          * @method getElement
20339          * @param {String} id the id of the elment to get
20340          * @return {Object} The element
20341          * @deprecated use Roo.getDom instead
20342          * @static
20343          */
20344         getElement: function(id) {
20345             return Roo.getDom(id);
20346         },
20347
20348         /**
20349          * Returns the style property for the DOM element (i.e.,
20350          * document.getElById(id).style)
20351          * @method getCss
20352          * @param {String} id the id of the elment to get
20353          * @return {Object} The style property of the element
20354          * @deprecated use Roo.getDom instead
20355          * @static
20356          */
20357         getCss: function(id) {
20358             var el = Roo.getDom(id);
20359             return (el) ? el.style : null;
20360         },
20361
20362         /**
20363          * Inner class for cached elements
20364          * @class DragDropMgr.ElementWrapper
20365          * @for DragDropMgr
20366          * @private
20367          * @deprecated
20368          */
20369         ElementWrapper: function(el) {
20370                 /**
20371                  * The element
20372                  * @property el
20373                  */
20374                 this.el = el || null;
20375                 /**
20376                  * The element id
20377                  * @property id
20378                  */
20379                 this.id = this.el && el.id;
20380                 /**
20381                  * A reference to the style property
20382                  * @property css
20383                  */
20384                 this.css = this.el && el.style;
20385             },
20386
20387         /**
20388          * Returns the X position of an html element
20389          * @method getPosX
20390          * @param el the element for which to get the position
20391          * @return {int} the X coordinate
20392          * @for DragDropMgr
20393          * @deprecated use Roo.lib.Dom.getX instead
20394          * @static
20395          */
20396         getPosX: function(el) {
20397             return Roo.lib.Dom.getX(el);
20398         },
20399
20400         /**
20401          * Returns the Y position of an html element
20402          * @method getPosY
20403          * @param el the element for which to get the position
20404          * @return {int} the Y coordinate
20405          * @deprecated use Roo.lib.Dom.getY instead
20406          * @static
20407          */
20408         getPosY: function(el) {
20409             return Roo.lib.Dom.getY(el);
20410         },
20411
20412         /**
20413          * Swap two nodes.  In IE, we use the native method, for others we
20414          * emulate the IE behavior
20415          * @method swapNode
20416          * @param n1 the first node to swap
20417          * @param n2 the other node to swap
20418          * @static
20419          */
20420         swapNode: function(n1, n2) {
20421             if (n1.swapNode) {
20422                 n1.swapNode(n2);
20423             } else {
20424                 var p = n2.parentNode;
20425                 var s = n2.nextSibling;
20426
20427                 if (s == n1) {
20428                     p.insertBefore(n1, n2);
20429                 } else if (n2 == n1.nextSibling) {
20430                     p.insertBefore(n2, n1);
20431                 } else {
20432                     n1.parentNode.replaceChild(n2, n1);
20433                     p.insertBefore(n1, s);
20434                 }
20435             }
20436         },
20437
20438         /**
20439          * Returns the current scroll position
20440          * @method getScroll
20441          * @private
20442          * @static
20443          */
20444         getScroll: function () {
20445             var t, l, dde=document.documentElement, db=document.body;
20446             if (dde && (dde.scrollTop || dde.scrollLeft)) {
20447                 t = dde.scrollTop;
20448                 l = dde.scrollLeft;
20449             } else if (db) {
20450                 t = db.scrollTop;
20451                 l = db.scrollLeft;
20452             } else {
20453
20454             }
20455             return { top: t, left: l };
20456         },
20457
20458         /**
20459          * Returns the specified element style property
20460          * @method getStyle
20461          * @param {HTMLElement} el          the element
20462          * @param {string}      styleProp   the style property
20463          * @return {string} The value of the style property
20464          * @deprecated use Roo.lib.Dom.getStyle
20465          * @static
20466          */
20467         getStyle: function(el, styleProp) {
20468             return Roo.fly(el).getStyle(styleProp);
20469         },
20470
20471         /**
20472          * Gets the scrollTop
20473          * @method getScrollTop
20474          * @return {int} the document's scrollTop
20475          * @static
20476          */
20477         getScrollTop: function () { return this.getScroll().top; },
20478
20479         /**
20480          * Gets the scrollLeft
20481          * @method getScrollLeft
20482          * @return {int} the document's scrollTop
20483          * @static
20484          */
20485         getScrollLeft: function () { return this.getScroll().left; },
20486
20487         /**
20488          * Sets the x/y position of an element to the location of the
20489          * target element.
20490          * @method moveToEl
20491          * @param {HTMLElement} moveEl      The element to move
20492          * @param {HTMLElement} targetEl    The position reference element
20493          * @static
20494          */
20495         moveToEl: function (moveEl, targetEl) {
20496             var aCoord = Roo.lib.Dom.getXY(targetEl);
20497             Roo.lib.Dom.setXY(moveEl, aCoord);
20498         },
20499
20500         /**
20501          * Numeric array sort function
20502          * @method numericSort
20503          * @static
20504          */
20505         numericSort: function(a, b) { return (a - b); },
20506
20507         /**
20508          * Internal counter
20509          * @property _timeoutCount
20510          * @private
20511          * @static
20512          */
20513         _timeoutCount: 0,
20514
20515         /**
20516          * Trying to make the load order less important.  Without this we get
20517          * an error if this file is loaded before the Event Utility.
20518          * @method _addListeners
20519          * @private
20520          * @static
20521          */
20522         _addListeners: function() {
20523             var DDM = Roo.dd.DDM;
20524             if ( Roo.lib.Event && document ) {
20525                 DDM._onLoad();
20526             } else {
20527                 if (DDM._timeoutCount > 2000) {
20528                 } else {
20529                     setTimeout(DDM._addListeners, 10);
20530                     if (document && document.body) {
20531                         DDM._timeoutCount += 1;
20532                     }
20533                 }
20534             }
20535         },
20536
20537         /**
20538          * Recursively searches the immediate parent and all child nodes for
20539          * the handle element in order to determine wheter or not it was
20540          * clicked.
20541          * @method handleWasClicked
20542          * @param node the html element to inspect
20543          * @static
20544          */
20545         handleWasClicked: function(node, id) {
20546             if (this.isHandle(id, node.id)) {
20547                 return true;
20548             } else {
20549                 // check to see if this is a text node child of the one we want
20550                 var p = node.parentNode;
20551
20552                 while (p) {
20553                     if (this.isHandle(id, p.id)) {
20554                         return true;
20555                     } else {
20556                         p = p.parentNode;
20557                     }
20558                 }
20559             }
20560
20561             return false;
20562         }
20563
20564     };
20565
20566 }();
20567
20568 // shorter alias, save a few bytes
20569 Roo.dd.DDM = Roo.dd.DragDropMgr;
20570 Roo.dd.DDM._addListeners();
20571
20572 }/*
20573  * Based on:
20574  * Ext JS Library 1.1.1
20575  * Copyright(c) 2006-2007, Ext JS, LLC.
20576  *
20577  * Originally Released Under LGPL - original licence link has changed is not relivant.
20578  *
20579  * Fork - LGPL
20580  * <script type="text/javascript">
20581  */
20582
20583 /**
20584  * @class Roo.dd.DD
20585  * A DragDrop implementation where the linked element follows the
20586  * mouse cursor during a drag.
20587  * @extends Roo.dd.DragDrop
20588  * @constructor
20589  * @param {String} id the id of the linked element
20590  * @param {String} sGroup the group of related DragDrop items
20591  * @param {object} config an object containing configurable attributes
20592  *                Valid properties for DD:
20593  *                    scroll
20594  */
20595 Roo.dd.DD = function(id, sGroup, config) {
20596     if (id) {
20597         this.init(id, sGroup, config);
20598     }
20599 };
20600
20601 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
20602
20603     /**
20604      * When set to true, the utility automatically tries to scroll the browser
20605      * window wehn a drag and drop element is dragged near the viewport boundary.
20606      * Defaults to true.
20607      * @property scroll
20608      * @type boolean
20609      */
20610     scroll: true,
20611
20612     /**
20613      * Sets the pointer offset to the distance between the linked element's top
20614      * left corner and the location the element was clicked
20615      * @method autoOffset
20616      * @param {int} iPageX the X coordinate of the click
20617      * @param {int} iPageY the Y coordinate of the click
20618      */
20619     autoOffset: function(iPageX, iPageY) {
20620         var x = iPageX - this.startPageX;
20621         var y = iPageY - this.startPageY;
20622         this.setDelta(x, y);
20623     },
20624
20625     /**
20626      * Sets the pointer offset.  You can call this directly to force the
20627      * offset to be in a particular location (e.g., pass in 0,0 to set it
20628      * to the center of the object)
20629      * @method setDelta
20630      * @param {int} iDeltaX the distance from the left
20631      * @param {int} iDeltaY the distance from the top
20632      */
20633     setDelta: function(iDeltaX, iDeltaY) {
20634         this.deltaX = iDeltaX;
20635         this.deltaY = iDeltaY;
20636     },
20637
20638     /**
20639      * Sets the drag element to the location of the mousedown or click event,
20640      * maintaining the cursor location relative to the location on the element
20641      * that was clicked.  Override this if you want to place the element in a
20642      * location other than where the cursor is.
20643      * @method setDragElPos
20644      * @param {int} iPageX the X coordinate of the mousedown or drag event
20645      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20646      */
20647     setDragElPos: function(iPageX, iPageY) {
20648         // the first time we do this, we are going to check to make sure
20649         // the element has css positioning
20650
20651         var el = this.getDragEl();
20652         this.alignElWithMouse(el, iPageX, iPageY);
20653     },
20654
20655     /**
20656      * Sets the element to the location of the mousedown or click event,
20657      * maintaining the cursor location relative to the location on the element
20658      * that was clicked.  Override this if you want to place the element in a
20659      * location other than where the cursor is.
20660      * @method alignElWithMouse
20661      * @param {HTMLElement} el the element to move
20662      * @param {int} iPageX the X coordinate of the mousedown or drag event
20663      * @param {int} iPageY the Y coordinate of the mousedown or drag event
20664      */
20665     alignElWithMouse: function(el, iPageX, iPageY) {
20666         var oCoord = this.getTargetCoord(iPageX, iPageY);
20667         var fly = el.dom ? el : Roo.fly(el);
20668         if (!this.deltaSetXY) {
20669             var aCoord = [oCoord.x, oCoord.y];
20670             fly.setXY(aCoord);
20671             var newLeft = fly.getLeft(true);
20672             var newTop  = fly.getTop(true);
20673             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
20674         } else {
20675             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
20676         }
20677
20678         this.cachePosition(oCoord.x, oCoord.y);
20679         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
20680         return oCoord;
20681     },
20682
20683     /**
20684      * Saves the most recent position so that we can reset the constraints and
20685      * tick marks on-demand.  We need to know this so that we can calculate the
20686      * number of pixels the element is offset from its original position.
20687      * @method cachePosition
20688      * @param iPageX the current x position (optional, this just makes it so we
20689      * don't have to look it up again)
20690      * @param iPageY the current y position (optional, this just makes it so we
20691      * don't have to look it up again)
20692      */
20693     cachePosition: function(iPageX, iPageY) {
20694         if (iPageX) {
20695             this.lastPageX = iPageX;
20696             this.lastPageY = iPageY;
20697         } else {
20698             var aCoord = Roo.lib.Dom.getXY(this.getEl());
20699             this.lastPageX = aCoord[0];
20700             this.lastPageY = aCoord[1];
20701         }
20702     },
20703
20704     /**
20705      * Auto-scroll the window if the dragged object has been moved beyond the
20706      * visible window boundary.
20707      * @method autoScroll
20708      * @param {int} x the drag element's x position
20709      * @param {int} y the drag element's y position
20710      * @param {int} h the height of the drag element
20711      * @param {int} w the width of the drag element
20712      * @private
20713      */
20714     autoScroll: function(x, y, h, w) {
20715
20716         if (this.scroll) {
20717             // The client height
20718             var clientH = Roo.lib.Dom.getViewWidth();
20719
20720             // The client width
20721             var clientW = Roo.lib.Dom.getViewHeight();
20722
20723             // The amt scrolled down
20724             var st = this.DDM.getScrollTop();
20725
20726             // The amt scrolled right
20727             var sl = this.DDM.getScrollLeft();
20728
20729             // Location of the bottom of the element
20730             var bot = h + y;
20731
20732             // Location of the right of the element
20733             var right = w + x;
20734
20735             // The distance from the cursor to the bottom of the visible area,
20736             // adjusted so that we don't scroll if the cursor is beyond the
20737             // element drag constraints
20738             var toBot = (clientH + st - y - this.deltaY);
20739
20740             // The distance from the cursor to the right of the visible area
20741             var toRight = (clientW + sl - x - this.deltaX);
20742
20743
20744             // How close to the edge the cursor must be before we scroll
20745             // var thresh = (document.all) ? 100 : 40;
20746             var thresh = 40;
20747
20748             // How many pixels to scroll per autoscroll op.  This helps to reduce
20749             // clunky scrolling. IE is more sensitive about this ... it needs this
20750             // value to be higher.
20751             var scrAmt = (document.all) ? 80 : 30;
20752
20753             // Scroll down if we are near the bottom of the visible page and the
20754             // obj extends below the crease
20755             if ( bot > clientH && toBot < thresh ) {
20756                 window.scrollTo(sl, st + scrAmt);
20757             }
20758
20759             // Scroll up if the window is scrolled down and the top of the object
20760             // goes above the top border
20761             if ( y < st && st > 0 && y - st < thresh ) {
20762                 window.scrollTo(sl, st - scrAmt);
20763             }
20764
20765             // Scroll right if the obj is beyond the right border and the cursor is
20766             // near the border.
20767             if ( right > clientW && toRight < thresh ) {
20768                 window.scrollTo(sl + scrAmt, st);
20769             }
20770
20771             // Scroll left if the window has been scrolled to the right and the obj
20772             // extends past the left border
20773             if ( x < sl && sl > 0 && x - sl < thresh ) {
20774                 window.scrollTo(sl - scrAmt, st);
20775             }
20776         }
20777     },
20778
20779     /**
20780      * Finds the location the element should be placed if we want to move
20781      * it to where the mouse location less the click offset would place us.
20782      * @method getTargetCoord
20783      * @param {int} iPageX the X coordinate of the click
20784      * @param {int} iPageY the Y coordinate of the click
20785      * @return an object that contains the coordinates (Object.x and Object.y)
20786      * @private
20787      */
20788     getTargetCoord: function(iPageX, iPageY) {
20789
20790
20791         var x = iPageX - this.deltaX;
20792         var y = iPageY - this.deltaY;
20793
20794         if (this.constrainX) {
20795             if (x < this.minX) { x = this.minX; }
20796             if (x > this.maxX) { x = this.maxX; }
20797         }
20798
20799         if (this.constrainY) {
20800             if (y < this.minY) { y = this.minY; }
20801             if (y > this.maxY) { y = this.maxY; }
20802         }
20803
20804         x = this.getTick(x, this.xTicks);
20805         y = this.getTick(y, this.yTicks);
20806
20807
20808         return {x:x, y:y};
20809     },
20810
20811     /*
20812      * Sets up config options specific to this class. Overrides
20813      * Roo.dd.DragDrop, but all versions of this method through the
20814      * inheritance chain are called
20815      */
20816     applyConfig: function() {
20817         Roo.dd.DD.superclass.applyConfig.call(this);
20818         this.scroll = (this.config.scroll !== false);
20819     },
20820
20821     /*
20822      * Event that fires prior to the onMouseDown event.  Overrides
20823      * Roo.dd.DragDrop.
20824      */
20825     b4MouseDown: function(e) {
20826         // this.resetConstraints();
20827         this.autoOffset(e.getPageX(),
20828                             e.getPageY());
20829     },
20830
20831     /*
20832      * Event that fires prior to the onDrag event.  Overrides
20833      * Roo.dd.DragDrop.
20834      */
20835     b4Drag: function(e) {
20836         this.setDragElPos(e.getPageX(),
20837                             e.getPageY());
20838     },
20839
20840     toString: function() {
20841         return ("DD " + this.id);
20842     }
20843
20844     //////////////////////////////////////////////////////////////////////////
20845     // Debugging ygDragDrop events that can be overridden
20846     //////////////////////////////////////////////////////////////////////////
20847     /*
20848     startDrag: function(x, y) {
20849     },
20850
20851     onDrag: function(e) {
20852     },
20853
20854     onDragEnter: function(e, id) {
20855     },
20856
20857     onDragOver: function(e, id) {
20858     },
20859
20860     onDragOut: function(e, id) {
20861     },
20862
20863     onDragDrop: function(e, id) {
20864     },
20865
20866     endDrag: function(e) {
20867     }
20868
20869     */
20870
20871 });/*
20872  * Based on:
20873  * Ext JS Library 1.1.1
20874  * Copyright(c) 2006-2007, Ext JS, LLC.
20875  *
20876  * Originally Released Under LGPL - original licence link has changed is not relivant.
20877  *
20878  * Fork - LGPL
20879  * <script type="text/javascript">
20880  */
20881
20882 /**
20883  * @class Roo.dd.DDProxy
20884  * A DragDrop implementation that inserts an empty, bordered div into
20885  * the document that follows the cursor during drag operations.  At the time of
20886  * the click, the frame div is resized to the dimensions of the linked html
20887  * element, and moved to the exact location of the linked element.
20888  *
20889  * References to the "frame" element refer to the single proxy element that
20890  * was created to be dragged in place of all DDProxy elements on the
20891  * page.
20892  *
20893  * @extends Roo.dd.DD
20894  * @constructor
20895  * @param {String} id the id of the linked html element
20896  * @param {String} sGroup the group of related DragDrop objects
20897  * @param {object} config an object containing configurable attributes
20898  *                Valid properties for DDProxy in addition to those in DragDrop:
20899  *                   resizeFrame, centerFrame, dragElId
20900  */
20901 Roo.dd.DDProxy = function(id, sGroup, config) {
20902     if (id) {
20903         this.init(id, sGroup, config);
20904         this.initFrame();
20905     }
20906 };
20907
20908 /**
20909  * The default drag frame div id
20910  * @property Roo.dd.DDProxy.dragElId
20911  * @type String
20912  * @static
20913  */
20914 Roo.dd.DDProxy.dragElId = "ygddfdiv";
20915
20916 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
20917
20918     /**
20919      * By default we resize the drag frame to be the same size as the element
20920      * we want to drag (this is to get the frame effect).  We can turn it off
20921      * if we want a different behavior.
20922      * @property resizeFrame
20923      * @type boolean
20924      */
20925     resizeFrame: true,
20926
20927     /**
20928      * By default the frame is positioned exactly where the drag element is, so
20929      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
20930      * you do not have constraints on the obj is to have the drag frame centered
20931      * around the cursor.  Set centerFrame to true for this effect.
20932      * @property centerFrame
20933      * @type boolean
20934      */
20935     centerFrame: false,
20936
20937     /**
20938      * Creates the proxy element if it does not yet exist
20939      * @method createFrame
20940      */
20941     createFrame: function() {
20942         var self = this;
20943         var body = document.body;
20944
20945         if (!body || !body.firstChild) {
20946             setTimeout( function() { self.createFrame(); }, 50 );
20947             return;
20948         }
20949
20950         var div = this.getDragEl();
20951
20952         if (!div) {
20953             div    = document.createElement("div");
20954             div.id = this.dragElId;
20955             var s  = div.style;
20956
20957             s.position   = "absolute";
20958             s.visibility = "hidden";
20959             s.cursor     = "move";
20960             s.border     = "2px solid #aaa";
20961             s.zIndex     = 999;
20962
20963             // appendChild can blow up IE if invoked prior to the window load event
20964             // while rendering a table.  It is possible there are other scenarios
20965             // that would cause this to happen as well.
20966             body.insertBefore(div, body.firstChild);
20967         }
20968     },
20969
20970     /**
20971      * Initialization for the drag frame element.  Must be called in the
20972      * constructor of all subclasses
20973      * @method initFrame
20974      */
20975     initFrame: function() {
20976         this.createFrame();
20977     },
20978
20979     applyConfig: function() {
20980         Roo.dd.DDProxy.superclass.applyConfig.call(this);
20981
20982         this.resizeFrame = (this.config.resizeFrame !== false);
20983         this.centerFrame = (this.config.centerFrame);
20984         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
20985     },
20986
20987     /**
20988      * Resizes the drag frame to the dimensions of the clicked object, positions
20989      * it over the object, and finally displays it
20990      * @method showFrame
20991      * @param {int} iPageX X click position
20992      * @param {int} iPageY Y click position
20993      * @private
20994      */
20995     showFrame: function(iPageX, iPageY) {
20996         var el = this.getEl();
20997         var dragEl = this.getDragEl();
20998         var s = dragEl.style;
20999
21000         this._resizeProxy();
21001
21002         if (this.centerFrame) {
21003             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21004                            Math.round(parseInt(s.height, 10)/2) );
21005         }
21006
21007         this.setDragElPos(iPageX, iPageY);
21008
21009         Roo.fly(dragEl).show();
21010     },
21011
21012     /**
21013      * The proxy is automatically resized to the dimensions of the linked
21014      * element when a drag is initiated, unless resizeFrame is set to false
21015      * @method _resizeProxy
21016      * @private
21017      */
21018     _resizeProxy: function() {
21019         if (this.resizeFrame) {
21020             var el = this.getEl();
21021             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21022         }
21023     },
21024
21025     // overrides Roo.dd.DragDrop
21026     b4MouseDown: function(e) {
21027         var x = e.getPageX();
21028         var y = e.getPageY();
21029         this.autoOffset(x, y);
21030         this.setDragElPos(x, y);
21031     },
21032
21033     // overrides Roo.dd.DragDrop
21034     b4StartDrag: function(x, y) {
21035         // show the drag frame
21036         this.showFrame(x, y);
21037     },
21038
21039     // overrides Roo.dd.DragDrop
21040     b4EndDrag: function(e) {
21041         Roo.fly(this.getDragEl()).hide();
21042     },
21043
21044     // overrides Roo.dd.DragDrop
21045     // By default we try to move the element to the last location of the frame.
21046     // This is so that the default behavior mirrors that of Roo.dd.DD.
21047     endDrag: function(e) {
21048
21049         var lel = this.getEl();
21050         var del = this.getDragEl();
21051
21052         // Show the drag frame briefly so we can get its position
21053         del.style.visibility = "";
21054
21055         this.beforeMove();
21056         // Hide the linked element before the move to get around a Safari
21057         // rendering bug.
21058         lel.style.visibility = "hidden";
21059         Roo.dd.DDM.moveToEl(lel, del);
21060         del.style.visibility = "hidden";
21061         lel.style.visibility = "";
21062
21063         this.afterDrag();
21064     },
21065
21066     beforeMove : function(){
21067
21068     },
21069
21070     afterDrag : function(){
21071
21072     },
21073
21074     toString: function() {
21075         return ("DDProxy " + this.id);
21076     }
21077
21078 });
21079 /*
21080  * Based on:
21081  * Ext JS Library 1.1.1
21082  * Copyright(c) 2006-2007, Ext JS, LLC.
21083  *
21084  * Originally Released Under LGPL - original licence link has changed is not relivant.
21085  *
21086  * Fork - LGPL
21087  * <script type="text/javascript">
21088  */
21089
21090  /**
21091  * @class Roo.dd.DDTarget
21092  * A DragDrop implementation that does not move, but can be a drop
21093  * target.  You would get the same result by simply omitting implementation
21094  * for the event callbacks, but this way we reduce the processing cost of the
21095  * event listener and the callbacks.
21096  * @extends Roo.dd.DragDrop
21097  * @constructor
21098  * @param {String} id the id of the element that is a drop target
21099  * @param {String} sGroup the group of related DragDrop objects
21100  * @param {object} config an object containing configurable attributes
21101  *                 Valid properties for DDTarget in addition to those in
21102  *                 DragDrop:
21103  *                    none
21104  */
21105 Roo.dd.DDTarget = function(id, sGroup, config) {
21106     if (id) {
21107         this.initTarget(id, sGroup, config);
21108     }
21109     if (config && (config.listeners || config.events)) { 
21110         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21111             listeners : config.listeners || {}, 
21112             events : config.events || {} 
21113         });    
21114     }
21115 };
21116
21117 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21118 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21119     toString: function() {
21120         return ("DDTarget " + this.id);
21121     }
21122 });
21123 /*
21124  * Based on:
21125  * Ext JS Library 1.1.1
21126  * Copyright(c) 2006-2007, Ext JS, LLC.
21127  *
21128  * Originally Released Under LGPL - original licence link has changed is not relivant.
21129  *
21130  * Fork - LGPL
21131  * <script type="text/javascript">
21132  */
21133  
21134
21135 /**
21136  * @class Roo.dd.ScrollManager
21137  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21138  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21139  * @singleton
21140  */
21141 Roo.dd.ScrollManager = function(){
21142     var ddm = Roo.dd.DragDropMgr;
21143     var els = {};
21144     var dragEl = null;
21145     var proc = {};
21146     
21147     
21148     
21149     var onStop = function(e){
21150         dragEl = null;
21151         clearProc();
21152     };
21153     
21154     var triggerRefresh = function(){
21155         if(ddm.dragCurrent){
21156              ddm.refreshCache(ddm.dragCurrent.groups);
21157         }
21158     };
21159     
21160     var doScroll = function(){
21161         if(ddm.dragCurrent){
21162             var dds = Roo.dd.ScrollManager;
21163             if(!dds.animate){
21164                 if(proc.el.scroll(proc.dir, dds.increment)){
21165                     triggerRefresh();
21166                 }
21167             }else{
21168                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21169             }
21170         }
21171     };
21172     
21173     var clearProc = function(){
21174         if(proc.id){
21175             clearInterval(proc.id);
21176         }
21177         proc.id = 0;
21178         proc.el = null;
21179         proc.dir = "";
21180     };
21181     
21182     var startProc = function(el, dir){
21183          Roo.log('scroll startproc');
21184         clearProc();
21185         proc.el = el;
21186         proc.dir = dir;
21187         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21188     };
21189     
21190     var onFire = function(e, isDrop){
21191        
21192         if(isDrop || !ddm.dragCurrent){ return; }
21193         var dds = Roo.dd.ScrollManager;
21194         if(!dragEl || dragEl != ddm.dragCurrent){
21195             dragEl = ddm.dragCurrent;
21196             // refresh regions on drag start
21197             dds.refreshCache();
21198         }
21199         
21200         var xy = Roo.lib.Event.getXY(e);
21201         var pt = new Roo.lib.Point(xy[0], xy[1]);
21202         for(var id in els){
21203             var el = els[id], r = el._region;
21204             if(r && r.contains(pt) && el.isScrollable()){
21205                 if(r.bottom - pt.y <= dds.thresh){
21206                     if(proc.el != el){
21207                         startProc(el, "down");
21208                     }
21209                     return;
21210                 }else if(r.right - pt.x <= dds.thresh){
21211                     if(proc.el != el){
21212                         startProc(el, "left");
21213                     }
21214                     return;
21215                 }else if(pt.y - r.top <= dds.thresh){
21216                     if(proc.el != el){
21217                         startProc(el, "up");
21218                     }
21219                     return;
21220                 }else if(pt.x - r.left <= dds.thresh){
21221                     if(proc.el != el){
21222                         startProc(el, "right");
21223                     }
21224                     return;
21225                 }
21226             }
21227         }
21228         clearProc();
21229     };
21230     
21231     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21232     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21233     
21234     return {
21235         /**
21236          * Registers new overflow element(s) to auto scroll
21237          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21238          */
21239         register : function(el){
21240             if(el instanceof Array){
21241                 for(var i = 0, len = el.length; i < len; i++) {
21242                         this.register(el[i]);
21243                 }
21244             }else{
21245                 el = Roo.get(el);
21246                 els[el.id] = el;
21247             }
21248             Roo.dd.ScrollManager.els = els;
21249         },
21250         
21251         /**
21252          * Unregisters overflow element(s) so they are no longer scrolled
21253          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21254          */
21255         unregister : function(el){
21256             if(el instanceof Array){
21257                 for(var i = 0, len = el.length; i < len; i++) {
21258                         this.unregister(el[i]);
21259                 }
21260             }else{
21261                 el = Roo.get(el);
21262                 delete els[el.id];
21263             }
21264         },
21265         
21266         /**
21267          * The number of pixels from the edge of a container the pointer needs to be to 
21268          * trigger scrolling (defaults to 25)
21269          * @type Number
21270          */
21271         thresh : 25,
21272         
21273         /**
21274          * The number of pixels to scroll in each scroll increment (defaults to 50)
21275          * @type Number
21276          */
21277         increment : 100,
21278         
21279         /**
21280          * The frequency of scrolls in milliseconds (defaults to 500)
21281          * @type Number
21282          */
21283         frequency : 500,
21284         
21285         /**
21286          * True to animate the scroll (defaults to true)
21287          * @type Boolean
21288          */
21289         animate: true,
21290         
21291         /**
21292          * The animation duration in seconds - 
21293          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21294          * @type Number
21295          */
21296         animDuration: .4,
21297         
21298         /**
21299          * Manually trigger a cache refresh.
21300          */
21301         refreshCache : function(){
21302             for(var id in els){
21303                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21304                     els[id]._region = els[id].getRegion();
21305                 }
21306             }
21307         }
21308     };
21309 }();/*
21310  * Based on:
21311  * Ext JS Library 1.1.1
21312  * Copyright(c) 2006-2007, Ext JS, LLC.
21313  *
21314  * Originally Released Under LGPL - original licence link has changed is not relivant.
21315  *
21316  * Fork - LGPL
21317  * <script type="text/javascript">
21318  */
21319  
21320
21321 /**
21322  * @class Roo.dd.Registry
21323  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21324  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21325  * @singleton
21326  */
21327 Roo.dd.Registry = function(){
21328     var elements = {}; 
21329     var handles = {}; 
21330     var autoIdSeed = 0;
21331
21332     var getId = function(el, autogen){
21333         if(typeof el == "string"){
21334             return el;
21335         }
21336         var id = el.id;
21337         if(!id && autogen !== false){
21338             id = "roodd-" + (++autoIdSeed);
21339             el.id = id;
21340         }
21341         return id;
21342     };
21343     
21344     return {
21345     /**
21346      * Register a drag drop element
21347      * @param {String|HTMLElement} element The id or DOM node to register
21348      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21349      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21350      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21351      * populated in the data object (if applicable):
21352      * <pre>
21353 Value      Description<br />
21354 ---------  ------------------------------------------<br />
21355 handles    Array of DOM nodes that trigger dragging<br />
21356            for the element being registered<br />
21357 isHandle   True if the element passed in triggers<br />
21358            dragging itself, else false
21359 </pre>
21360      */
21361         register : function(el, data){
21362             data = data || {};
21363             if(typeof el == "string"){
21364                 el = document.getElementById(el);
21365             }
21366             data.ddel = el;
21367             elements[getId(el)] = data;
21368             if(data.isHandle !== false){
21369                 handles[data.ddel.id] = data;
21370             }
21371             if(data.handles){
21372                 var hs = data.handles;
21373                 for(var i = 0, len = hs.length; i < len; i++){
21374                         handles[getId(hs[i])] = data;
21375                 }
21376             }
21377         },
21378
21379     /**
21380      * Unregister a drag drop element
21381      * @param {String|HTMLElement}  element The id or DOM node to unregister
21382      */
21383         unregister : function(el){
21384             var id = getId(el, false);
21385             var data = elements[id];
21386             if(data){
21387                 delete elements[id];
21388                 if(data.handles){
21389                     var hs = data.handles;
21390                     for(var i = 0, len = hs.length; i < len; i++){
21391                         delete handles[getId(hs[i], false)];
21392                     }
21393                 }
21394             }
21395         },
21396
21397     /**
21398      * Returns the handle registered for a DOM Node by id
21399      * @param {String|HTMLElement} id The DOM node or id to look up
21400      * @return {Object} handle The custom handle data
21401      */
21402         getHandle : function(id){
21403             if(typeof id != "string"){ // must be element?
21404                 id = id.id;
21405             }
21406             return handles[id];
21407         },
21408
21409     /**
21410      * Returns the handle that is registered for the DOM node that is the target of the event
21411      * @param {Event} e The event
21412      * @return {Object} handle The custom handle data
21413      */
21414         getHandleFromEvent : function(e){
21415             var t = Roo.lib.Event.getTarget(e);
21416             return t ? handles[t.id] : null;
21417         },
21418
21419     /**
21420      * Returns a custom data object that is registered for a DOM node by id
21421      * @param {String|HTMLElement} id The DOM node or id to look up
21422      * @return {Object} data The custom data
21423      */
21424         getTarget : function(id){
21425             if(typeof id != "string"){ // must be element?
21426                 id = id.id;
21427             }
21428             return elements[id];
21429         },
21430
21431     /**
21432      * Returns a custom data object that is registered for the DOM node that is the target of the event
21433      * @param {Event} e The event
21434      * @return {Object} data The custom data
21435      */
21436         getTargetFromEvent : function(e){
21437             var t = Roo.lib.Event.getTarget(e);
21438             return t ? elements[t.id] || handles[t.id] : null;
21439         }
21440     };
21441 }();/*
21442  * Based on:
21443  * Ext JS Library 1.1.1
21444  * Copyright(c) 2006-2007, Ext JS, LLC.
21445  *
21446  * Originally Released Under LGPL - original licence link has changed is not relivant.
21447  *
21448  * Fork - LGPL
21449  * <script type="text/javascript">
21450  */
21451  
21452
21453 /**
21454  * @class Roo.dd.StatusProxy
21455  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
21456  * default drag proxy used by all Roo.dd components.
21457  * @constructor
21458  * @param {Object} config
21459  */
21460 Roo.dd.StatusProxy = function(config){
21461     Roo.apply(this, config);
21462     this.id = this.id || Roo.id();
21463     this.el = new Roo.Layer({
21464         dh: {
21465             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
21466                 {tag: "div", cls: "x-dd-drop-icon"},
21467                 {tag: "div", cls: "x-dd-drag-ghost"}
21468             ]
21469         }, 
21470         shadow: !config || config.shadow !== false
21471     });
21472     this.ghost = Roo.get(this.el.dom.childNodes[1]);
21473     this.dropStatus = this.dropNotAllowed;
21474 };
21475
21476 Roo.dd.StatusProxy.prototype = {
21477     /**
21478      * @cfg {String} dropAllowed
21479      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
21480      */
21481     dropAllowed : "x-dd-drop-ok",
21482     /**
21483      * @cfg {String} dropNotAllowed
21484      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
21485      */
21486     dropNotAllowed : "x-dd-drop-nodrop",
21487
21488     /**
21489      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
21490      * over the current target element.
21491      * @param {String} cssClass The css class for the new drop status indicator image
21492      */
21493     setStatus : function(cssClass){
21494         cssClass = cssClass || this.dropNotAllowed;
21495         if(this.dropStatus != cssClass){
21496             this.el.replaceClass(this.dropStatus, cssClass);
21497             this.dropStatus = cssClass;
21498         }
21499     },
21500
21501     /**
21502      * Resets the status indicator to the default dropNotAllowed value
21503      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
21504      */
21505     reset : function(clearGhost){
21506         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
21507         this.dropStatus = this.dropNotAllowed;
21508         if(clearGhost){
21509             this.ghost.update("");
21510         }
21511     },
21512
21513     /**
21514      * Updates the contents of the ghost element
21515      * @param {String} html The html that will replace the current innerHTML of the ghost element
21516      */
21517     update : function(html){
21518         if(typeof html == "string"){
21519             this.ghost.update(html);
21520         }else{
21521             this.ghost.update("");
21522             html.style.margin = "0";
21523             this.ghost.dom.appendChild(html);
21524         }
21525         // ensure float = none set?? cant remember why though.
21526         var el = this.ghost.dom.firstChild;
21527                 if(el){
21528                         Roo.fly(el).setStyle('float', 'none');
21529                 }
21530     },
21531     
21532     /**
21533      * Returns the underlying proxy {@link Roo.Layer}
21534      * @return {Roo.Layer} el
21535     */
21536     getEl : function(){
21537         return this.el;
21538     },
21539
21540     /**
21541      * Returns the ghost element
21542      * @return {Roo.Element} el
21543      */
21544     getGhost : function(){
21545         return this.ghost;
21546     },
21547
21548     /**
21549      * Hides the proxy
21550      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
21551      */
21552     hide : function(clear){
21553         this.el.hide();
21554         if(clear){
21555             this.reset(true);
21556         }
21557     },
21558
21559     /**
21560      * Stops the repair animation if it's currently running
21561      */
21562     stop : function(){
21563         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
21564             this.anim.stop();
21565         }
21566     },
21567
21568     /**
21569      * Displays this proxy
21570      */
21571     show : function(){
21572         this.el.show();
21573     },
21574
21575     /**
21576      * Force the Layer to sync its shadow and shim positions to the element
21577      */
21578     sync : function(){
21579         this.el.sync();
21580     },
21581
21582     /**
21583      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
21584      * invalid drop operation by the item being dragged.
21585      * @param {Array} xy The XY position of the element ([x, y])
21586      * @param {Function} callback The function to call after the repair is complete
21587      * @param {Object} scope The scope in which to execute the callback
21588      */
21589     repair : function(xy, callback, scope){
21590         this.callback = callback;
21591         this.scope = scope;
21592         if(xy && this.animRepair !== false){
21593             this.el.addClass("x-dd-drag-repair");
21594             this.el.hideUnders(true);
21595             this.anim = this.el.shift({
21596                 duration: this.repairDuration || .5,
21597                 easing: 'easeOut',
21598                 xy: xy,
21599                 stopFx: true,
21600                 callback: this.afterRepair,
21601                 scope: this
21602             });
21603         }else{
21604             this.afterRepair();
21605         }
21606     },
21607
21608     // private
21609     afterRepair : function(){
21610         this.hide(true);
21611         if(typeof this.callback == "function"){
21612             this.callback.call(this.scope || this);
21613         }
21614         this.callback = null;
21615         this.scope = null;
21616     }
21617 };/*
21618  * Based on:
21619  * Ext JS Library 1.1.1
21620  * Copyright(c) 2006-2007, Ext JS, LLC.
21621  *
21622  * Originally Released Under LGPL - original licence link has changed is not relivant.
21623  *
21624  * Fork - LGPL
21625  * <script type="text/javascript">
21626  */
21627
21628 /**
21629  * @class Roo.dd.DragSource
21630  * @extends Roo.dd.DDProxy
21631  * A simple class that provides the basic implementation needed to make any element draggable.
21632  * @constructor
21633  * @param {String/HTMLElement/Element} el The container element
21634  * @param {Object} config
21635  */
21636 Roo.dd.DragSource = function(el, config){
21637     this.el = Roo.get(el);
21638     this.dragData = {};
21639     
21640     Roo.apply(this, config);
21641     
21642     if(!this.proxy){
21643         this.proxy = new Roo.dd.StatusProxy();
21644     }
21645
21646     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
21647           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
21648     
21649     this.dragging = false;
21650 };
21651
21652 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
21653     /**
21654      * @cfg {String} dropAllowed
21655      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
21656      */
21657     dropAllowed : "x-dd-drop-ok",
21658     /**
21659      * @cfg {String} dropNotAllowed
21660      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
21661      */
21662     dropNotAllowed : "x-dd-drop-nodrop",
21663
21664     /**
21665      * Returns the data object associated with this drag source
21666      * @return {Object} data An object containing arbitrary data
21667      */
21668     getDragData : function(e){
21669         return this.dragData;
21670     },
21671
21672     // private
21673     onDragEnter : function(e, id){
21674         var target = Roo.dd.DragDropMgr.getDDById(id);
21675         this.cachedTarget = target;
21676         if(this.beforeDragEnter(target, e, id) !== false){
21677             if(target.isNotifyTarget){
21678                 var status = target.notifyEnter(this, e, this.dragData);
21679                 this.proxy.setStatus(status);
21680             }else{
21681                 this.proxy.setStatus(this.dropAllowed);
21682             }
21683             
21684             if(this.afterDragEnter){
21685                 /**
21686                  * An empty function by default, but provided so that you can perform a custom action
21687                  * when the dragged item enters the drop target by providing an implementation.
21688                  * @param {Roo.dd.DragDrop} target The drop target
21689                  * @param {Event} e The event object
21690                  * @param {String} id The id of the dragged element
21691                  * @method afterDragEnter
21692                  */
21693                 this.afterDragEnter(target, e, id);
21694             }
21695         }
21696     },
21697
21698     /**
21699      * An empty function by default, but provided so that you can perform a custom action
21700      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
21701      * @param {Roo.dd.DragDrop} target The drop target
21702      * @param {Event} e The event object
21703      * @param {String} id The id of the dragged element
21704      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21705      */
21706     beforeDragEnter : function(target, e, id){
21707         return true;
21708     },
21709
21710     // private
21711     alignElWithMouse: function() {
21712         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
21713         this.proxy.sync();
21714     },
21715
21716     // private
21717     onDragOver : function(e, id){
21718         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21719         if(this.beforeDragOver(target, e, id) !== false){
21720             if(target.isNotifyTarget){
21721                 var status = target.notifyOver(this, e, this.dragData);
21722                 this.proxy.setStatus(status);
21723             }
21724
21725             if(this.afterDragOver){
21726                 /**
21727                  * An empty function by default, but provided so that you can perform a custom action
21728                  * while the dragged item is over the drop target by providing an implementation.
21729                  * @param {Roo.dd.DragDrop} target The drop target
21730                  * @param {Event} e The event object
21731                  * @param {String} id The id of the dragged element
21732                  * @method afterDragOver
21733                  */
21734                 this.afterDragOver(target, e, id);
21735             }
21736         }
21737     },
21738
21739     /**
21740      * An empty function by default, but provided so that you can perform a custom action
21741      * while the dragged item is over the drop target and optionally cancel the onDragOver.
21742      * @param {Roo.dd.DragDrop} target The drop target
21743      * @param {Event} e The event object
21744      * @param {String} id The id of the dragged element
21745      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21746      */
21747     beforeDragOver : function(target, e, id){
21748         return true;
21749     },
21750
21751     // private
21752     onDragOut : function(e, id){
21753         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21754         if(this.beforeDragOut(target, e, id) !== false){
21755             if(target.isNotifyTarget){
21756                 target.notifyOut(this, e, this.dragData);
21757             }
21758             this.proxy.reset();
21759             if(this.afterDragOut){
21760                 /**
21761                  * An empty function by default, but provided so that you can perform a custom action
21762                  * after the dragged item is dragged out of the target without dropping.
21763                  * @param {Roo.dd.DragDrop} target The drop target
21764                  * @param {Event} e The event object
21765                  * @param {String} id The id of the dragged element
21766                  * @method afterDragOut
21767                  */
21768                 this.afterDragOut(target, e, id);
21769             }
21770         }
21771         this.cachedTarget = null;
21772     },
21773
21774     /**
21775      * An empty function by default, but provided so that you can perform a custom action before the dragged
21776      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
21777      * @param {Roo.dd.DragDrop} target The drop target
21778      * @param {Event} e The event object
21779      * @param {String} id The id of the dragged element
21780      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21781      */
21782     beforeDragOut : function(target, e, id){
21783         return true;
21784     },
21785     
21786     // private
21787     onDragDrop : function(e, id){
21788         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
21789         if(this.beforeDragDrop(target, e, id) !== false){
21790             if(target.isNotifyTarget){
21791                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
21792                     this.onValidDrop(target, e, id);
21793                 }else{
21794                     this.onInvalidDrop(target, e, id);
21795                 }
21796             }else{
21797                 this.onValidDrop(target, e, id);
21798             }
21799             
21800             if(this.afterDragDrop){
21801                 /**
21802                  * An empty function by default, but provided so that you can perform a custom action
21803                  * after a valid drag drop has occurred by providing an implementation.
21804                  * @param {Roo.dd.DragDrop} target The drop target
21805                  * @param {Event} e The event object
21806                  * @param {String} id The id of the dropped element
21807                  * @method afterDragDrop
21808                  */
21809                 this.afterDragDrop(target, e, id);
21810             }
21811         }
21812         delete this.cachedTarget;
21813     },
21814
21815     /**
21816      * An empty function by default, but provided so that you can perform a custom action before the dragged
21817      * item is dropped onto the target and optionally cancel the onDragDrop.
21818      * @param {Roo.dd.DragDrop} target The drop target
21819      * @param {Event} e The event object
21820      * @param {String} id The id of the dragged element
21821      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
21822      */
21823     beforeDragDrop : function(target, e, id){
21824         return true;
21825     },
21826
21827     // private
21828     onValidDrop : function(target, e, id){
21829         this.hideProxy();
21830         if(this.afterValidDrop){
21831             /**
21832              * An empty function by default, but provided so that you can perform a custom action
21833              * after a valid drop has occurred by providing an implementation.
21834              * @param {Object} target The target DD 
21835              * @param {Event} e The event object
21836              * @param {String} id The id of the dropped element
21837              * @method afterInvalidDrop
21838              */
21839             this.afterValidDrop(target, e, id);
21840         }
21841     },
21842
21843     // private
21844     getRepairXY : function(e, data){
21845         return this.el.getXY();  
21846     },
21847
21848     // private
21849     onInvalidDrop : function(target, e, id){
21850         this.beforeInvalidDrop(target, e, id);
21851         if(this.cachedTarget){
21852             if(this.cachedTarget.isNotifyTarget){
21853                 this.cachedTarget.notifyOut(this, e, this.dragData);
21854             }
21855             this.cacheTarget = null;
21856         }
21857         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
21858
21859         if(this.afterInvalidDrop){
21860             /**
21861              * An empty function by default, but provided so that you can perform a custom action
21862              * after an invalid drop has occurred by providing an implementation.
21863              * @param {Event} e The event object
21864              * @param {String} id The id of the dropped element
21865              * @method afterInvalidDrop
21866              */
21867             this.afterInvalidDrop(e, id);
21868         }
21869     },
21870
21871     // private
21872     afterRepair : function(){
21873         if(Roo.enableFx){
21874             this.el.highlight(this.hlColor || "c3daf9");
21875         }
21876         this.dragging = false;
21877     },
21878
21879     /**
21880      * An empty function by default, but provided so that you can perform a custom action after an invalid
21881      * drop has occurred.
21882      * @param {Roo.dd.DragDrop} target The drop target
21883      * @param {Event} e The event object
21884      * @param {String} id The id of the dragged element
21885      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
21886      */
21887     beforeInvalidDrop : function(target, e, id){
21888         return true;
21889     },
21890
21891     // private
21892     handleMouseDown : function(e){
21893         if(this.dragging) {
21894             return;
21895         }
21896         var data = this.getDragData(e);
21897         if(data && this.onBeforeDrag(data, e) !== false){
21898             this.dragData = data;
21899             this.proxy.stop();
21900             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
21901         } 
21902     },
21903
21904     /**
21905      * An empty function by default, but provided so that you can perform a custom action before the initial
21906      * drag event begins and optionally cancel it.
21907      * @param {Object} data An object containing arbitrary data to be shared with drop targets
21908      * @param {Event} e The event object
21909      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
21910      */
21911     onBeforeDrag : function(data, e){
21912         return true;
21913     },
21914
21915     /**
21916      * An empty function by default, but provided so that you can perform a custom action once the initial
21917      * drag event has begun.  The drag cannot be canceled from this function.
21918      * @param {Number} x The x position of the click on the dragged object
21919      * @param {Number} y The y position of the click on the dragged object
21920      */
21921     onStartDrag : Roo.emptyFn,
21922
21923     // private - YUI override
21924     startDrag : function(x, y){
21925         this.proxy.reset();
21926         this.dragging = true;
21927         this.proxy.update("");
21928         this.onInitDrag(x, y);
21929         this.proxy.show();
21930     },
21931
21932     // private
21933     onInitDrag : function(x, y){
21934         var clone = this.el.dom.cloneNode(true);
21935         clone.id = Roo.id(); // prevent duplicate ids
21936         this.proxy.update(clone);
21937         this.onStartDrag(x, y);
21938         return true;
21939     },
21940
21941     /**
21942      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
21943      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
21944      */
21945     getProxy : function(){
21946         return this.proxy;  
21947     },
21948
21949     /**
21950      * Hides the drag source's {@link Roo.dd.StatusProxy}
21951      */
21952     hideProxy : function(){
21953         this.proxy.hide();  
21954         this.proxy.reset(true);
21955         this.dragging = false;
21956     },
21957
21958     // private
21959     triggerCacheRefresh : function(){
21960         Roo.dd.DDM.refreshCache(this.groups);
21961     },
21962
21963     // private - override to prevent hiding
21964     b4EndDrag: function(e) {
21965     },
21966
21967     // private - override to prevent moving
21968     endDrag : function(e){
21969         this.onEndDrag(this.dragData, e);
21970     },
21971
21972     // private
21973     onEndDrag : function(data, e){
21974     },
21975     
21976     // private - pin to cursor
21977     autoOffset : function(x, y) {
21978         this.setDelta(-12, -20);
21979     }    
21980 });/*
21981  * Based on:
21982  * Ext JS Library 1.1.1
21983  * Copyright(c) 2006-2007, Ext JS, LLC.
21984  *
21985  * Originally Released Under LGPL - original licence link has changed is not relivant.
21986  *
21987  * Fork - LGPL
21988  * <script type="text/javascript">
21989  */
21990
21991
21992 /**
21993  * @class Roo.dd.DropTarget
21994  * @extends Roo.dd.DDTarget
21995  * A simple class that provides the basic implementation needed to make any element a drop target that can have
21996  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
21997  * @constructor
21998  * @param {String/HTMLElement/Element} el The container element
21999  * @param {Object} config
22000  */
22001 Roo.dd.DropTarget = function(el, config){
22002     this.el = Roo.get(el);
22003     
22004     var listeners = false; ;
22005     if (config && config.listeners) {
22006         listeners= config.listeners;
22007         delete config.listeners;
22008     }
22009     Roo.apply(this, config);
22010     
22011     if(this.containerScroll){
22012         Roo.dd.ScrollManager.register(this.el);
22013     }
22014     this.addEvents( {
22015          /**
22016          * @scope Roo.dd.DropTarget
22017          */
22018          
22019          /**
22020          * @event enter
22021          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22022          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22023          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22024          * 
22025          * IMPORTANT : it should set this.overClass and this.dropAllowed
22026          * 
22027          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22028          * @param {Event} e The event
22029          * @param {Object} data An object containing arbitrary data supplied by the drag source
22030          */
22031         "enter" : true,
22032         
22033          /**
22034          * @event over
22035          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22036          * This method will be called on every mouse movement while the drag source is over the drop target.
22037          * This default implementation simply returns the dropAllowed config value.
22038          * 
22039          * IMPORTANT : it should set this.dropAllowed
22040          * 
22041          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22042          * @param {Event} e The event
22043          * @param {Object} data An object containing arbitrary data supplied by the drag source
22044          
22045          */
22046         "over" : true,
22047         /**
22048          * @event out
22049          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22050          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22051          * overClass (if any) from the drop element.
22052          * 
22053          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22054          * @param {Event} e The event
22055          * @param {Object} data An object containing arbitrary data supplied by the drag source
22056          */
22057          "out" : true,
22058          
22059         /**
22060          * @event drop
22061          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22062          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22063          * implementation that does something to process the drop event and returns true so that the drag source's
22064          * repair action does not run.
22065          * 
22066          * IMPORTANT : it should set this.success
22067          * 
22068          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22069          * @param {Event} e The event
22070          * @param {Object} data An object containing arbitrary data supplied by the drag source
22071         */
22072          "drop" : true
22073     });
22074             
22075      
22076     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22077         this.el.dom, 
22078         this.ddGroup || this.group,
22079         {
22080             isTarget: true,
22081             listeners : listeners || {} 
22082            
22083         
22084         }
22085     );
22086
22087 };
22088
22089 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22090     /**
22091      * @cfg {String} overClass
22092      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22093      */
22094      /**
22095      * @cfg {String} ddGroup
22096      * The drag drop group to handle drop events for
22097      */
22098      
22099     /**
22100      * @cfg {String} dropAllowed
22101      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22102      */
22103     dropAllowed : "x-dd-drop-ok",
22104     /**
22105      * @cfg {String} dropNotAllowed
22106      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22107      */
22108     dropNotAllowed : "x-dd-drop-nodrop",
22109     /**
22110      * @cfg {boolean} success
22111      * set this after drop listener.. 
22112      */
22113     success : false,
22114     /**
22115      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22116      * if the drop point is valid for over/enter..
22117      */
22118     valid : false,
22119     // private
22120     isTarget : true,
22121
22122     // private
22123     isNotifyTarget : true,
22124     
22125     /**
22126      * @hide
22127      */
22128     notifyEnter : function(dd, e, data)
22129     {
22130         this.valid = true;
22131         this.fireEvent('enter', dd, e, data);
22132         if(this.overClass){
22133             this.el.addClass(this.overClass);
22134         }
22135         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22136             this.valid ? this.dropAllowed : this.dropNotAllowed
22137         );
22138     },
22139
22140     /**
22141      * @hide
22142      */
22143     notifyOver : function(dd, e, data)
22144     {
22145         this.valid = true;
22146         this.fireEvent('over', dd, e, data);
22147         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22148             this.valid ? this.dropAllowed : this.dropNotAllowed
22149         );
22150     },
22151
22152     /**
22153      * @hide
22154      */
22155     notifyOut : function(dd, e, data)
22156     {
22157         this.fireEvent('out', dd, e, data);
22158         if(this.overClass){
22159             this.el.removeClass(this.overClass);
22160         }
22161     },
22162
22163     /**
22164      * @hide
22165      */
22166     notifyDrop : function(dd, e, data)
22167     {
22168         this.success = false;
22169         this.fireEvent('drop', dd, e, data);
22170         return this.success;
22171     }
22172 });/*
22173  * Based on:
22174  * Ext JS Library 1.1.1
22175  * Copyright(c) 2006-2007, Ext JS, LLC.
22176  *
22177  * Originally Released Under LGPL - original licence link has changed is not relivant.
22178  *
22179  * Fork - LGPL
22180  * <script type="text/javascript">
22181  */
22182
22183
22184 /**
22185  * @class Roo.dd.DragZone
22186  * @extends Roo.dd.DragSource
22187  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22188  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22189  * @constructor
22190  * @param {String/HTMLElement/Element} el The container element
22191  * @param {Object} config
22192  */
22193 Roo.dd.DragZone = function(el, config){
22194     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22195     if(this.containerScroll){
22196         Roo.dd.ScrollManager.register(this.el);
22197     }
22198 };
22199
22200 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22201     /**
22202      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22203      * for auto scrolling during drag operations.
22204      */
22205     /**
22206      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22207      * method after a failed drop (defaults to "c3daf9" - light blue)
22208      */
22209
22210     /**
22211      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22212      * for a valid target to drag based on the mouse down. Override this method
22213      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22214      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22215      * @param {EventObject} e The mouse down event
22216      * @return {Object} The dragData
22217      */
22218     getDragData : function(e){
22219         return Roo.dd.Registry.getHandleFromEvent(e);
22220     },
22221     
22222     /**
22223      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22224      * this.dragData.ddel
22225      * @param {Number} x The x position of the click on the dragged object
22226      * @param {Number} y The y position of the click on the dragged object
22227      * @return {Boolean} true to continue the drag, false to cancel
22228      */
22229     onInitDrag : function(x, y){
22230         this.proxy.update(this.dragData.ddel.cloneNode(true));
22231         this.onStartDrag(x, y);
22232         return true;
22233     },
22234     
22235     /**
22236      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22237      */
22238     afterRepair : function(){
22239         if(Roo.enableFx){
22240             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22241         }
22242         this.dragging = false;
22243     },
22244
22245     /**
22246      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22247      * the XY of this.dragData.ddel
22248      * @param {EventObject} e The mouse up event
22249      * @return {Array} The xy location (e.g. [100, 200])
22250      */
22251     getRepairXY : function(e){
22252         return Roo.Element.fly(this.dragData.ddel).getXY();  
22253     }
22254 });/*
22255  * Based on:
22256  * Ext JS Library 1.1.1
22257  * Copyright(c) 2006-2007, Ext JS, LLC.
22258  *
22259  * Originally Released Under LGPL - original licence link has changed is not relivant.
22260  *
22261  * Fork - LGPL
22262  * <script type="text/javascript">
22263  */
22264 /**
22265  * @class Roo.dd.DropZone
22266  * @extends Roo.dd.DropTarget
22267  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22268  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22269  * @constructor
22270  * @param {String/HTMLElement/Element} el The container element
22271  * @param {Object} config
22272  */
22273 Roo.dd.DropZone = function(el, config){
22274     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22275 };
22276
22277 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22278     /**
22279      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22280      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22281      * provide your own custom lookup.
22282      * @param {Event} e The event
22283      * @return {Object} data The custom data
22284      */
22285     getTargetFromEvent : function(e){
22286         return Roo.dd.Registry.getTargetFromEvent(e);
22287     },
22288
22289     /**
22290      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22291      * that it has registered.  This method has no default implementation and should be overridden to provide
22292      * node-specific processing if necessary.
22293      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22294      * {@link #getTargetFromEvent} for this node)
22295      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22296      * @param {Event} e The event
22297      * @param {Object} data An object containing arbitrary data supplied by the drag source
22298      */
22299     onNodeEnter : function(n, dd, e, data){
22300         
22301     },
22302
22303     /**
22304      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22305      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22306      * overridden to provide the proper feedback.
22307      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22308      * {@link #getTargetFromEvent} for this node)
22309      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22310      * @param {Event} e The event
22311      * @param {Object} data An object containing arbitrary data supplied by the drag source
22312      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22313      * underlying {@link Roo.dd.StatusProxy} can be updated
22314      */
22315     onNodeOver : function(n, dd, e, data){
22316         return this.dropAllowed;
22317     },
22318
22319     /**
22320      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22321      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22322      * node-specific processing if necessary.
22323      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22324      * {@link #getTargetFromEvent} for this node)
22325      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22326      * @param {Event} e The event
22327      * @param {Object} data An object containing arbitrary data supplied by the drag source
22328      */
22329     onNodeOut : function(n, dd, e, data){
22330         
22331     },
22332
22333     /**
22334      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22335      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22336      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22337      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22338      * {@link #getTargetFromEvent} for this node)
22339      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22340      * @param {Event} e The event
22341      * @param {Object} data An object containing arbitrary data supplied by the drag source
22342      * @return {Boolean} True if the drop was valid, else false
22343      */
22344     onNodeDrop : function(n, dd, e, data){
22345         return false;
22346     },
22347
22348     /**
22349      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22350      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22351      * it should be overridden to provide the proper feedback if necessary.
22352      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22353      * @param {Event} e The event
22354      * @param {Object} data An object containing arbitrary data supplied by the drag source
22355      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22356      * underlying {@link Roo.dd.StatusProxy} can be updated
22357      */
22358     onContainerOver : function(dd, e, data){
22359         return this.dropNotAllowed;
22360     },
22361
22362     /**
22363      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
22364      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
22365      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
22366      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
22367      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22368      * @param {Event} e The event
22369      * @param {Object} data An object containing arbitrary data supplied by the drag source
22370      * @return {Boolean} True if the drop was valid, else false
22371      */
22372     onContainerDrop : function(dd, e, data){
22373         return false;
22374     },
22375
22376     /**
22377      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
22378      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
22379      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
22380      * you should override this method and provide a custom implementation.
22381      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22382      * @param {Event} e The event
22383      * @param {Object} data An object containing arbitrary data supplied by the drag source
22384      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22385      * underlying {@link Roo.dd.StatusProxy} can be updated
22386      */
22387     notifyEnter : function(dd, e, data){
22388         return this.dropNotAllowed;
22389     },
22390
22391     /**
22392      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
22393      * This method will be called on every mouse movement while the drag source is over the drop zone.
22394      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
22395      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
22396      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
22397      * registered node, it will call {@link #onContainerOver}.
22398      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22399      * @param {Event} e The event
22400      * @param {Object} data An object containing arbitrary data supplied by the drag source
22401      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22402      * underlying {@link Roo.dd.StatusProxy} can be updated
22403      */
22404     notifyOver : function(dd, e, data){
22405         var n = this.getTargetFromEvent(e);
22406         if(!n){ // not over valid drop target
22407             if(this.lastOverNode){
22408                 this.onNodeOut(this.lastOverNode, dd, e, data);
22409                 this.lastOverNode = null;
22410             }
22411             return this.onContainerOver(dd, e, data);
22412         }
22413         if(this.lastOverNode != n){
22414             if(this.lastOverNode){
22415                 this.onNodeOut(this.lastOverNode, dd, e, data);
22416             }
22417             this.onNodeEnter(n, dd, e, data);
22418             this.lastOverNode = n;
22419         }
22420         return this.onNodeOver(n, dd, e, data);
22421     },
22422
22423     /**
22424      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
22425      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
22426      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
22427      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22428      * @param {Event} e The event
22429      * @param {Object} data An object containing arbitrary data supplied by the drag zone
22430      */
22431     notifyOut : function(dd, e, data){
22432         if(this.lastOverNode){
22433             this.onNodeOut(this.lastOverNode, dd, e, data);
22434             this.lastOverNode = null;
22435         }
22436     },
22437
22438     /**
22439      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
22440      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
22441      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
22442      * otherwise it will call {@link #onContainerDrop}.
22443      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22444      * @param {Event} e The event
22445      * @param {Object} data An object containing arbitrary data supplied by the drag source
22446      * @return {Boolean} True if the drop was valid, else false
22447      */
22448     notifyDrop : function(dd, e, data){
22449         if(this.lastOverNode){
22450             this.onNodeOut(this.lastOverNode, dd, e, data);
22451             this.lastOverNode = null;
22452         }
22453         var n = this.getTargetFromEvent(e);
22454         return n ?
22455             this.onNodeDrop(n, dd, e, data) :
22456             this.onContainerDrop(dd, e, data);
22457     },
22458
22459     // private
22460     triggerCacheRefresh : function(){
22461         Roo.dd.DDM.refreshCache(this.groups);
22462     }  
22463 });/*
22464  * Based on:
22465  * Ext JS Library 1.1.1
22466  * Copyright(c) 2006-2007, Ext JS, LLC.
22467  *
22468  * Originally Released Under LGPL - original licence link has changed is not relivant.
22469  *
22470  * Fork - LGPL
22471  * <script type="text/javascript">
22472  */
22473
22474
22475 /**
22476  * @class Roo.data.SortTypes
22477  * @singleton
22478  * Defines the default sorting (casting?) comparison functions used when sorting data.
22479  */
22480 Roo.data.SortTypes = {
22481     /**
22482      * Default sort that does nothing
22483      * @param {Mixed} s The value being converted
22484      * @return {Mixed} The comparison value
22485      */
22486     none : function(s){
22487         return s;
22488     },
22489     
22490     /**
22491      * The regular expression used to strip tags
22492      * @type {RegExp}
22493      * @property
22494      */
22495     stripTagsRE : /<\/?[^>]+>/gi,
22496     
22497     /**
22498      * Strips all HTML tags to sort on text only
22499      * @param {Mixed} s The value being converted
22500      * @return {String} The comparison value
22501      */
22502     asText : function(s){
22503         return String(s).replace(this.stripTagsRE, "");
22504     },
22505     
22506     /**
22507      * Strips all HTML tags to sort on text only - Case insensitive
22508      * @param {Mixed} s The value being converted
22509      * @return {String} The comparison value
22510      */
22511     asUCText : function(s){
22512         return String(s).toUpperCase().replace(this.stripTagsRE, "");
22513     },
22514     
22515     /**
22516      * Case insensitive string
22517      * @param {Mixed} s The value being converted
22518      * @return {String} The comparison value
22519      */
22520     asUCString : function(s) {
22521         return String(s).toUpperCase();
22522     },
22523     
22524     /**
22525      * Date sorting
22526      * @param {Mixed} s The value being converted
22527      * @return {Number} The comparison value
22528      */
22529     asDate : function(s) {
22530         if(!s){
22531             return 0;
22532         }
22533         if(s instanceof Date){
22534             return s.getTime();
22535         }
22536         return Date.parse(String(s));
22537     },
22538     
22539     /**
22540      * Float sorting
22541      * @param {Mixed} s The value being converted
22542      * @return {Float} The comparison value
22543      */
22544     asFloat : function(s) {
22545         var val = parseFloat(String(s).replace(/,/g, ""));
22546         if(isNaN(val)) {
22547             val = 0;
22548         }
22549         return val;
22550     },
22551     
22552     /**
22553      * Integer sorting
22554      * @param {Mixed} s The value being converted
22555      * @return {Number} The comparison value
22556      */
22557     asInt : function(s) {
22558         var val = parseInt(String(s).replace(/,/g, ""));
22559         if(isNaN(val)) {
22560             val = 0;
22561         }
22562         return val;
22563     }
22564 };/*
22565  * Based on:
22566  * Ext JS Library 1.1.1
22567  * Copyright(c) 2006-2007, Ext JS, LLC.
22568  *
22569  * Originally Released Under LGPL - original licence link has changed is not relivant.
22570  *
22571  * Fork - LGPL
22572  * <script type="text/javascript">
22573  */
22574
22575 /**
22576 * @class Roo.data.Record
22577  * Instances of this class encapsulate both record <em>definition</em> information, and record
22578  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
22579  * to access Records cached in an {@link Roo.data.Store} object.<br>
22580  * <p>
22581  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
22582  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
22583  * objects.<br>
22584  * <p>
22585  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
22586  * @constructor
22587  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
22588  * {@link #create}. The parameters are the same.
22589  * @param {Array} data An associative Array of data values keyed by the field name.
22590  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
22591  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
22592  * not specified an integer id is generated.
22593  */
22594 Roo.data.Record = function(data, id){
22595     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
22596     this.data = data;
22597 };
22598
22599 /**
22600  * Generate a constructor for a specific record layout.
22601  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
22602  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
22603  * Each field definition object may contain the following properties: <ul>
22604  * <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,
22605  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
22606  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
22607  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
22608  * is being used, then this is a string containing the javascript expression to reference the data relative to 
22609  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
22610  * to the data item relative to the record element. If the mapping expression is the same as the field name,
22611  * this may be omitted.</p></li>
22612  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
22613  * <ul><li>auto (Default, implies no conversion)</li>
22614  * <li>string</li>
22615  * <li>int</li>
22616  * <li>float</li>
22617  * <li>boolean</li>
22618  * <li>date</li></ul></p></li>
22619  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
22620  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
22621  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
22622  * by the Reader into an object that will be stored in the Record. It is passed the
22623  * following parameters:<ul>
22624  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
22625  * </ul></p></li>
22626  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
22627  * </ul>
22628  * <br>usage:<br><pre><code>
22629 var TopicRecord = Roo.data.Record.create(
22630     {name: 'title', mapping: 'topic_title'},
22631     {name: 'author', mapping: 'username'},
22632     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
22633     {name: 'lastPost', mapping: 'post_time', type: 'date'},
22634     {name: 'lastPoster', mapping: 'user2'},
22635     {name: 'excerpt', mapping: 'post_text'}
22636 );
22637
22638 var myNewRecord = new TopicRecord({
22639     title: 'Do my job please',
22640     author: 'noobie',
22641     totalPosts: 1,
22642     lastPost: new Date(),
22643     lastPoster: 'Animal',
22644     excerpt: 'No way dude!'
22645 });
22646 myStore.add(myNewRecord);
22647 </code></pre>
22648  * @method create
22649  * @static
22650  */
22651 Roo.data.Record.create = function(o){
22652     var f = function(){
22653         f.superclass.constructor.apply(this, arguments);
22654     };
22655     Roo.extend(f, Roo.data.Record);
22656     var p = f.prototype;
22657     p.fields = new Roo.util.MixedCollection(false, function(field){
22658         return field.name;
22659     });
22660     for(var i = 0, len = o.length; i < len; i++){
22661         p.fields.add(new Roo.data.Field(o[i]));
22662     }
22663     f.getField = function(name){
22664         return p.fields.get(name);  
22665     };
22666     return f;
22667 };
22668
22669 Roo.data.Record.AUTO_ID = 1000;
22670 Roo.data.Record.EDIT = 'edit';
22671 Roo.data.Record.REJECT = 'reject';
22672 Roo.data.Record.COMMIT = 'commit';
22673
22674 Roo.data.Record.prototype = {
22675     /**
22676      * Readonly flag - true if this record has been modified.
22677      * @type Boolean
22678      */
22679     dirty : false,
22680     editing : false,
22681     error: null,
22682     modified: null,
22683
22684     // private
22685     join : function(store){
22686         this.store = store;
22687     },
22688
22689     /**
22690      * Set the named field to the specified value.
22691      * @param {String} name The name of the field to set.
22692      * @param {Object} value The value to set the field to.
22693      */
22694     set : function(name, value){
22695         if(this.data[name] == value){
22696             return;
22697         }
22698         this.dirty = true;
22699         if(!this.modified){
22700             this.modified = {};
22701         }
22702         if(typeof this.modified[name] == 'undefined'){
22703             this.modified[name] = this.data[name];
22704         }
22705         this.data[name] = value;
22706         if(!this.editing && this.store){
22707             this.store.afterEdit(this);
22708         }       
22709     },
22710
22711     /**
22712      * Get the value of the named field.
22713      * @param {String} name The name of the field to get the value of.
22714      * @return {Object} The value of the field.
22715      */
22716     get : function(name){
22717         return this.data[name]; 
22718     },
22719
22720     // private
22721     beginEdit : function(){
22722         this.editing = true;
22723         this.modified = {}; 
22724     },
22725
22726     // private
22727     cancelEdit : function(){
22728         this.editing = false;
22729         delete this.modified;
22730     },
22731
22732     // private
22733     endEdit : function(){
22734         this.editing = false;
22735         if(this.dirty && this.store){
22736             this.store.afterEdit(this);
22737         }
22738     },
22739
22740     /**
22741      * Usually called by the {@link Roo.data.Store} which owns the Record.
22742      * Rejects all changes made to the Record since either creation, or the last commit operation.
22743      * Modified fields are reverted to their original values.
22744      * <p>
22745      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22746      * of reject operations.
22747      */
22748     reject : function(){
22749         var m = this.modified;
22750         for(var n in m){
22751             if(typeof m[n] != "function"){
22752                 this.data[n] = m[n];
22753             }
22754         }
22755         this.dirty = false;
22756         delete this.modified;
22757         this.editing = false;
22758         if(this.store){
22759             this.store.afterReject(this);
22760         }
22761     },
22762
22763     /**
22764      * Usually called by the {@link Roo.data.Store} which owns the Record.
22765      * Commits all changes made to the Record since either creation, or the last commit operation.
22766      * <p>
22767      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
22768      * of commit operations.
22769      */
22770     commit : function(){
22771         this.dirty = false;
22772         delete this.modified;
22773         this.editing = false;
22774         if(this.store){
22775             this.store.afterCommit(this);
22776         }
22777     },
22778
22779     // private
22780     hasError : function(){
22781         return this.error != null;
22782     },
22783
22784     // private
22785     clearError : function(){
22786         this.error = null;
22787     },
22788
22789     /**
22790      * Creates a copy of this record.
22791      * @param {String} id (optional) A new record id if you don't want to use this record's id
22792      * @return {Record}
22793      */
22794     copy : function(newId) {
22795         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
22796     }
22797 };/*
22798  * Based on:
22799  * Ext JS Library 1.1.1
22800  * Copyright(c) 2006-2007, Ext JS, LLC.
22801  *
22802  * Originally Released Under LGPL - original licence link has changed is not relivant.
22803  *
22804  * Fork - LGPL
22805  * <script type="text/javascript">
22806  */
22807
22808
22809
22810 /**
22811  * @class Roo.data.Store
22812  * @extends Roo.util.Observable
22813  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
22814  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
22815  * <p>
22816  * 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
22817  * has no knowledge of the format of the data returned by the Proxy.<br>
22818  * <p>
22819  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
22820  * instances from the data object. These records are cached and made available through accessor functions.
22821  * @constructor
22822  * Creates a new Store.
22823  * @param {Object} config A config object containing the objects needed for the Store to access data,
22824  * and read the data into Records.
22825  */
22826 Roo.data.Store = function(config){
22827     this.data = new Roo.util.MixedCollection(false);
22828     this.data.getKey = function(o){
22829         return o.id;
22830     };
22831     this.baseParams = {};
22832     // private
22833     this.paramNames = {
22834         "start" : "start",
22835         "limit" : "limit",
22836         "sort" : "sort",
22837         "dir" : "dir",
22838         "multisort" : "_multisort"
22839     };
22840
22841     if(config && config.data){
22842         this.inlineData = config.data;
22843         delete config.data;
22844     }
22845
22846     Roo.apply(this, config);
22847     
22848     if(this.reader){ // reader passed
22849         this.reader = Roo.factory(this.reader, Roo.data);
22850         this.reader.xmodule = this.xmodule || false;
22851         if(!this.recordType){
22852             this.recordType = this.reader.recordType;
22853         }
22854         if(this.reader.onMetaChange){
22855             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
22856         }
22857     }
22858
22859     if(this.recordType){
22860         this.fields = this.recordType.prototype.fields;
22861     }
22862     this.modified = [];
22863
22864     this.addEvents({
22865         /**
22866          * @event datachanged
22867          * Fires when the data cache has changed, and a widget which is using this Store
22868          * as a Record cache should refresh its view.
22869          * @param {Store} this
22870          */
22871         datachanged : true,
22872         /**
22873          * @event metachange
22874          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
22875          * @param {Store} this
22876          * @param {Object} meta The JSON metadata
22877          */
22878         metachange : true,
22879         /**
22880          * @event add
22881          * Fires when Records have been added to the Store
22882          * @param {Store} this
22883          * @param {Roo.data.Record[]} records The array of Records added
22884          * @param {Number} index The index at which the record(s) were added
22885          */
22886         add : true,
22887         /**
22888          * @event remove
22889          * Fires when a Record has been removed from the Store
22890          * @param {Store} this
22891          * @param {Roo.data.Record} record The Record that was removed
22892          * @param {Number} index The index at which the record was removed
22893          */
22894         remove : true,
22895         /**
22896          * @event update
22897          * Fires when a Record has been updated
22898          * @param {Store} this
22899          * @param {Roo.data.Record} record The Record that was updated
22900          * @param {String} operation The update operation being performed.  Value may be one of:
22901          * <pre><code>
22902  Roo.data.Record.EDIT
22903  Roo.data.Record.REJECT
22904  Roo.data.Record.COMMIT
22905          * </code></pre>
22906          */
22907         update : true,
22908         /**
22909          * @event clear
22910          * Fires when the data cache has been cleared.
22911          * @param {Store} this
22912          */
22913         clear : true,
22914         /**
22915          * @event beforeload
22916          * Fires before a request is made for a new data object.  If the beforeload handler returns false
22917          * the load action will be canceled.
22918          * @param {Store} this
22919          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22920          */
22921         beforeload : true,
22922         /**
22923          * @event beforeloadadd
22924          * Fires after a new set of Records has been loaded.
22925          * @param {Store} this
22926          * @param {Roo.data.Record[]} records The Records that were loaded
22927          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22928          */
22929         beforeloadadd : true,
22930         /**
22931          * @event load
22932          * Fires after a new set of Records has been loaded, before they are added to the store.
22933          * @param {Store} this
22934          * @param {Roo.data.Record[]} records The Records that were loaded
22935          * @param {Object} options The loading options that were specified (see {@link #load} for details)
22936          * @params {Object} return from reader
22937          */
22938         load : true,
22939         /**
22940          * @event loadexception
22941          * Fires if an exception occurs in the Proxy during loading.
22942          * Called with the signature of the Proxy's "loadexception" event.
22943          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
22944          * 
22945          * @param {Proxy} 
22946          * @param {Object} return from JsonData.reader() - success, totalRecords, records
22947          * @param {Object} load options 
22948          * @param {Object} jsonData from your request (normally this contains the Exception)
22949          */
22950         loadexception : true
22951     });
22952     
22953     if(this.proxy){
22954         this.proxy = Roo.factory(this.proxy, Roo.data);
22955         this.proxy.xmodule = this.xmodule || false;
22956         this.relayEvents(this.proxy,  ["loadexception"]);
22957     }
22958     this.sortToggle = {};
22959     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
22960
22961     Roo.data.Store.superclass.constructor.call(this);
22962
22963     if(this.inlineData){
22964         this.loadData(this.inlineData);
22965         delete this.inlineData;
22966     }
22967 };
22968
22969 Roo.extend(Roo.data.Store, Roo.util.Observable, {
22970      /**
22971     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
22972     * without a remote query - used by combo/forms at present.
22973     */
22974     
22975     /**
22976     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
22977     */
22978     /**
22979     * @cfg {Array} data Inline data to be loaded when the store is initialized.
22980     */
22981     /**
22982     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
22983     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
22984     */
22985     /**
22986     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
22987     * on any HTTP request
22988     */
22989     /**
22990     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
22991     */
22992     /**
22993     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
22994     */
22995     multiSort: false,
22996     /**
22997     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
22998     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
22999     */
23000     remoteSort : false,
23001
23002     /**
23003     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23004      * loaded or when a record is removed. (defaults to false).
23005     */
23006     pruneModifiedRecords : false,
23007
23008     // private
23009     lastOptions : null,
23010
23011     /**
23012      * Add Records to the Store and fires the add event.
23013      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23014      */
23015     add : function(records){
23016         records = [].concat(records);
23017         for(var i = 0, len = records.length; i < len; i++){
23018             records[i].join(this);
23019         }
23020         var index = this.data.length;
23021         this.data.addAll(records);
23022         this.fireEvent("add", this, records, index);
23023     },
23024
23025     /**
23026      * Remove a Record from the Store and fires the remove event.
23027      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23028      */
23029     remove : function(record){
23030         var index = this.data.indexOf(record);
23031         this.data.removeAt(index);
23032  
23033         if(this.pruneModifiedRecords){
23034             this.modified.remove(record);
23035         }
23036         this.fireEvent("remove", this, record, index);
23037     },
23038
23039     /**
23040      * Remove all Records from the Store and fires the clear event.
23041      */
23042     removeAll : function(){
23043         this.data.clear();
23044         if(this.pruneModifiedRecords){
23045             this.modified = [];
23046         }
23047         this.fireEvent("clear", this);
23048     },
23049
23050     /**
23051      * Inserts Records to the Store at the given index and fires the add event.
23052      * @param {Number} index The start index at which to insert the passed Records.
23053      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23054      */
23055     insert : function(index, records){
23056         records = [].concat(records);
23057         for(var i = 0, len = records.length; i < len; i++){
23058             this.data.insert(index, records[i]);
23059             records[i].join(this);
23060         }
23061         this.fireEvent("add", this, records, index);
23062     },
23063
23064     /**
23065      * Get the index within the cache of the passed Record.
23066      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23067      * @return {Number} The index of the passed Record. Returns -1 if not found.
23068      */
23069     indexOf : function(record){
23070         return this.data.indexOf(record);
23071     },
23072
23073     /**
23074      * Get the index within the cache of the Record with the passed id.
23075      * @param {String} id The id of the Record to find.
23076      * @return {Number} The index of the Record. Returns -1 if not found.
23077      */
23078     indexOfId : function(id){
23079         return this.data.indexOfKey(id);
23080     },
23081
23082     /**
23083      * Get the Record with the specified id.
23084      * @param {String} id The id of the Record to find.
23085      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23086      */
23087     getById : function(id){
23088         return this.data.key(id);
23089     },
23090
23091     /**
23092      * Get the Record at the specified index.
23093      * @param {Number} index The index of the Record to find.
23094      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23095      */
23096     getAt : function(index){
23097         return this.data.itemAt(index);
23098     },
23099
23100     /**
23101      * Returns a range of Records between specified indices.
23102      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23103      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23104      * @return {Roo.data.Record[]} An array of Records
23105      */
23106     getRange : function(start, end){
23107         return this.data.getRange(start, end);
23108     },
23109
23110     // private
23111     storeOptions : function(o){
23112         o = Roo.apply({}, o);
23113         delete o.callback;
23114         delete o.scope;
23115         this.lastOptions = o;
23116     },
23117
23118     /**
23119      * Loads the Record cache from the configured Proxy using the configured Reader.
23120      * <p>
23121      * If using remote paging, then the first load call must specify the <em>start</em>
23122      * and <em>limit</em> properties in the options.params property to establish the initial
23123      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23124      * <p>
23125      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23126      * and this call will return before the new data has been loaded. Perform any post-processing
23127      * in a callback function, or in a "load" event handler.</strong>
23128      * <p>
23129      * @param {Object} options An object containing properties which control loading options:<ul>
23130      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23131      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23132      * passed the following arguments:<ul>
23133      * <li>r : Roo.data.Record[]</li>
23134      * <li>options: Options object from the load call</li>
23135      * <li>success: Boolean success indicator</li></ul></li>
23136      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23137      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23138      * </ul>
23139      */
23140     load : function(options){
23141         options = options || {};
23142         if(this.fireEvent("beforeload", this, options) !== false){
23143             this.storeOptions(options);
23144             var p = Roo.apply(options.params || {}, this.baseParams);
23145             // if meta was not loaded from remote source.. try requesting it.
23146             if (!this.reader.metaFromRemote) {
23147                 p._requestMeta = 1;
23148             }
23149             if(this.sortInfo && this.remoteSort){
23150                 var pn = this.paramNames;
23151                 p[pn["sort"]] = this.sortInfo.field;
23152                 p[pn["dir"]] = this.sortInfo.direction;
23153             }
23154             if (this.multiSort) {
23155                 var pn = this.paramNames;
23156                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23157             }
23158             
23159             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23160         }
23161     },
23162
23163     /**
23164      * Reloads the Record cache from the configured Proxy using the configured Reader and
23165      * the options from the last load operation performed.
23166      * @param {Object} options (optional) An object containing properties which may override the options
23167      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23168      * the most recently used options are reused).
23169      */
23170     reload : function(options){
23171         this.load(Roo.applyIf(options||{}, this.lastOptions));
23172     },
23173
23174     // private
23175     // Called as a callback by the Reader during a load operation.
23176     loadRecords : function(o, options, success){
23177         if(!o || success === false){
23178             if(success !== false){
23179                 this.fireEvent("load", this, [], options, o);
23180             }
23181             if(options.callback){
23182                 options.callback.call(options.scope || this, [], options, false);
23183             }
23184             return;
23185         }
23186         // if data returned failure - throw an exception.
23187         if (o.success === false) {
23188             // show a message if no listener is registered.
23189             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23190                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23191             }
23192             // loadmask wil be hooked into this..
23193             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23194             return;
23195         }
23196         var r = o.records, t = o.totalRecords || r.length;
23197         
23198         this.fireEvent("beforeloadadd", this, r, options, o);
23199         
23200         if(!options || options.add !== true){
23201             if(this.pruneModifiedRecords){
23202                 this.modified = [];
23203             }
23204             for(var i = 0, len = r.length; i < len; i++){
23205                 r[i].join(this);
23206             }
23207             if(this.snapshot){
23208                 this.data = this.snapshot;
23209                 delete this.snapshot;
23210             }
23211             this.data.clear();
23212             this.data.addAll(r);
23213             this.totalLength = t;
23214             this.applySort();
23215             this.fireEvent("datachanged", this);
23216         }else{
23217             this.totalLength = Math.max(t, this.data.length+r.length);
23218             this.add(r);
23219         }
23220         
23221         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23222                 
23223             var e = new Roo.data.Record({});
23224
23225             e.set(this.parent.displayField, this.parent.emptyTitle);
23226             e.set(this.parent.valueField, '');
23227
23228             this.insert(0, e);
23229         }
23230             
23231         this.fireEvent("load", this, r, options, o);
23232         if(options.callback){
23233             options.callback.call(options.scope || this, r, options, true);
23234         }
23235     },
23236
23237
23238     /**
23239      * Loads data from a passed data block. A Reader which understands the format of the data
23240      * must have been configured in the constructor.
23241      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23242      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23243      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23244      */
23245     loadData : function(o, append){
23246         var r = this.reader.readRecords(o);
23247         this.loadRecords(r, {add: append}, true);
23248     },
23249
23250     /**
23251      * Gets the number of cached records.
23252      * <p>
23253      * <em>If using paging, this may not be the total size of the dataset. If the data object
23254      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23255      * the data set size</em>
23256      */
23257     getCount : function(){
23258         return this.data.length || 0;
23259     },
23260
23261     /**
23262      * Gets the total number of records in the dataset as returned by the server.
23263      * <p>
23264      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23265      * the dataset size</em>
23266      */
23267     getTotalCount : function(){
23268         return this.totalLength || 0;
23269     },
23270
23271     /**
23272      * Returns the sort state of the Store as an object with two properties:
23273      * <pre><code>
23274  field {String} The name of the field by which the Records are sorted
23275  direction {String} The sort order, "ASC" or "DESC"
23276      * </code></pre>
23277      */
23278     getSortState : function(){
23279         return this.sortInfo;
23280     },
23281
23282     // private
23283     applySort : function(){
23284         if(this.sortInfo && !this.remoteSort){
23285             var s = this.sortInfo, f = s.field;
23286             var st = this.fields.get(f).sortType;
23287             var fn = function(r1, r2){
23288                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23289                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23290             };
23291             this.data.sort(s.direction, fn);
23292             if(this.snapshot && this.snapshot != this.data){
23293                 this.snapshot.sort(s.direction, fn);
23294             }
23295         }
23296     },
23297
23298     /**
23299      * Sets the default sort column and order to be used by the next load operation.
23300      * @param {String} fieldName The name of the field to sort by.
23301      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23302      */
23303     setDefaultSort : function(field, dir){
23304         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23305     },
23306
23307     /**
23308      * Sort the Records.
23309      * If remote sorting is used, the sort is performed on the server, and the cache is
23310      * reloaded. If local sorting is used, the cache is sorted internally.
23311      * @param {String} fieldName The name of the field to sort by.
23312      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23313      */
23314     sort : function(fieldName, dir){
23315         var f = this.fields.get(fieldName);
23316         if(!dir){
23317             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23318             
23319             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23320                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23321             }else{
23322                 dir = f.sortDir;
23323             }
23324         }
23325         this.sortToggle[f.name] = dir;
23326         this.sortInfo = {field: f.name, direction: dir};
23327         if(!this.remoteSort){
23328             this.applySort();
23329             this.fireEvent("datachanged", this);
23330         }else{
23331             this.load(this.lastOptions);
23332         }
23333     },
23334
23335     /**
23336      * Calls the specified function for each of the Records in the cache.
23337      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23338      * Returning <em>false</em> aborts and exits the iteration.
23339      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23340      */
23341     each : function(fn, scope){
23342         this.data.each(fn, scope);
23343     },
23344
23345     /**
23346      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23347      * (e.g., during paging).
23348      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23349      */
23350     getModifiedRecords : function(){
23351         return this.modified;
23352     },
23353
23354     // private
23355     createFilterFn : function(property, value, anyMatch){
23356         if(!value.exec){ // not a regex
23357             value = String(value);
23358             if(value.length == 0){
23359                 return false;
23360             }
23361             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
23362         }
23363         return function(r){
23364             return value.test(r.data[property]);
23365         };
23366     },
23367
23368     /**
23369      * Sums the value of <i>property</i> for each record between start and end and returns the result.
23370      * @param {String} property A field on your records
23371      * @param {Number} start The record index to start at (defaults to 0)
23372      * @param {Number} end The last record index to include (defaults to length - 1)
23373      * @return {Number} The sum
23374      */
23375     sum : function(property, start, end){
23376         var rs = this.data.items, v = 0;
23377         start = start || 0;
23378         end = (end || end === 0) ? end : rs.length-1;
23379
23380         for(var i = start; i <= end; i++){
23381             v += (rs[i].data[property] || 0);
23382         }
23383         return v;
23384     },
23385
23386     /**
23387      * Filter the records by a specified property.
23388      * @param {String} field A field on your records
23389      * @param {String/RegExp} value Either a string that the field
23390      * should start with or a RegExp to test against the field
23391      * @param {Boolean} anyMatch True to match any part not just the beginning
23392      */
23393     filter : function(property, value, anyMatch){
23394         var fn = this.createFilterFn(property, value, anyMatch);
23395         return fn ? this.filterBy(fn) : this.clearFilter();
23396     },
23397
23398     /**
23399      * Filter by a function. The specified function will be called with each
23400      * record in this data source. If the function returns true the record is included,
23401      * otherwise it is filtered.
23402      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23403      * @param {Object} scope (optional) The scope of the function (defaults to this)
23404      */
23405     filterBy : function(fn, scope){
23406         this.snapshot = this.snapshot || this.data;
23407         this.data = this.queryBy(fn, scope||this);
23408         this.fireEvent("datachanged", this);
23409     },
23410
23411     /**
23412      * Query the records by a specified property.
23413      * @param {String} field A field on your records
23414      * @param {String/RegExp} value Either a string that the field
23415      * should start with or a RegExp to test against the field
23416      * @param {Boolean} anyMatch True to match any part not just the beginning
23417      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23418      */
23419     query : function(property, value, anyMatch){
23420         var fn = this.createFilterFn(property, value, anyMatch);
23421         return fn ? this.queryBy(fn) : this.data.clone();
23422     },
23423
23424     /**
23425      * Query by a function. The specified function will be called with each
23426      * record in this data source. If the function returns true the record is included
23427      * in the results.
23428      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
23429      * @param {Object} scope (optional) The scope of the function (defaults to this)
23430       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
23431      **/
23432     queryBy : function(fn, scope){
23433         var data = this.snapshot || this.data;
23434         return data.filterBy(fn, scope||this);
23435     },
23436
23437     /**
23438      * Collects unique values for a particular dataIndex from this store.
23439      * @param {String} dataIndex The property to collect
23440      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
23441      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
23442      * @return {Array} An array of the unique values
23443      **/
23444     collect : function(dataIndex, allowNull, bypassFilter){
23445         var d = (bypassFilter === true && this.snapshot) ?
23446                 this.snapshot.items : this.data.items;
23447         var v, sv, r = [], l = {};
23448         for(var i = 0, len = d.length; i < len; i++){
23449             v = d[i].data[dataIndex];
23450             sv = String(v);
23451             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
23452                 l[sv] = true;
23453                 r[r.length] = v;
23454             }
23455         }
23456         return r;
23457     },
23458
23459     /**
23460      * Revert to a view of the Record cache with no filtering applied.
23461      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
23462      */
23463     clearFilter : function(suppressEvent){
23464         if(this.snapshot && this.snapshot != this.data){
23465             this.data = this.snapshot;
23466             delete this.snapshot;
23467             if(suppressEvent !== true){
23468                 this.fireEvent("datachanged", this);
23469             }
23470         }
23471     },
23472
23473     // private
23474     afterEdit : function(record){
23475         if(this.modified.indexOf(record) == -1){
23476             this.modified.push(record);
23477         }
23478         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
23479     },
23480     
23481     // private
23482     afterReject : function(record){
23483         this.modified.remove(record);
23484         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
23485     },
23486
23487     // private
23488     afterCommit : function(record){
23489         this.modified.remove(record);
23490         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
23491     },
23492
23493     /**
23494      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
23495      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
23496      */
23497     commitChanges : function(){
23498         var m = this.modified.slice(0);
23499         this.modified = [];
23500         for(var i = 0, len = m.length; i < len; i++){
23501             m[i].commit();
23502         }
23503     },
23504
23505     /**
23506      * Cancel outstanding changes on all changed records.
23507      */
23508     rejectChanges : function(){
23509         var m = this.modified.slice(0);
23510         this.modified = [];
23511         for(var i = 0, len = m.length; i < len; i++){
23512             m[i].reject();
23513         }
23514     },
23515
23516     onMetaChange : function(meta, rtype, o){
23517         this.recordType = rtype;
23518         this.fields = rtype.prototype.fields;
23519         delete this.snapshot;
23520         this.sortInfo = meta.sortInfo || this.sortInfo;
23521         this.modified = [];
23522         this.fireEvent('metachange', this, this.reader.meta);
23523     },
23524     
23525     moveIndex : function(data, type)
23526     {
23527         var index = this.indexOf(data);
23528         
23529         var newIndex = index + type;
23530         
23531         this.remove(data);
23532         
23533         this.insert(newIndex, data);
23534         
23535     }
23536 });/*
23537  * Based on:
23538  * Ext JS Library 1.1.1
23539  * Copyright(c) 2006-2007, Ext JS, LLC.
23540  *
23541  * Originally Released Under LGPL - original licence link has changed is not relivant.
23542  *
23543  * Fork - LGPL
23544  * <script type="text/javascript">
23545  */
23546
23547 /**
23548  * @class Roo.data.SimpleStore
23549  * @extends Roo.data.Store
23550  * Small helper class to make creating Stores from Array data easier.
23551  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
23552  * @cfg {Array} fields An array of field definition objects, or field name strings.
23553  * @cfg {Object} an existing reader (eg. copied from another store)
23554  * @cfg {Array} data The multi-dimensional array of data
23555  * @constructor
23556  * @param {Object} config
23557  */
23558 Roo.data.SimpleStore = function(config)
23559 {
23560     Roo.data.SimpleStore.superclass.constructor.call(this, {
23561         isLocal : true,
23562         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
23563                 id: config.id
23564             },
23565             Roo.data.Record.create(config.fields)
23566         ),
23567         proxy : new Roo.data.MemoryProxy(config.data)
23568     });
23569     this.load();
23570 };
23571 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
23572  * Based on:
23573  * Ext JS Library 1.1.1
23574  * Copyright(c) 2006-2007, Ext JS, LLC.
23575  *
23576  * Originally Released Under LGPL - original licence link has changed is not relivant.
23577  *
23578  * Fork - LGPL
23579  * <script type="text/javascript">
23580  */
23581
23582 /**
23583 /**
23584  * @extends Roo.data.Store
23585  * @class Roo.data.JsonStore
23586  * Small helper class to make creating Stores for JSON data easier. <br/>
23587 <pre><code>
23588 var store = new Roo.data.JsonStore({
23589     url: 'get-images.php',
23590     root: 'images',
23591     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
23592 });
23593 </code></pre>
23594  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
23595  * JsonReader and HttpProxy (unless inline data is provided).</b>
23596  * @cfg {Array} fields An array of field definition objects, or field name strings.
23597  * @constructor
23598  * @param {Object} config
23599  */
23600 Roo.data.JsonStore = function(c){
23601     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
23602         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
23603         reader: new Roo.data.JsonReader(c, c.fields)
23604     }));
23605 };
23606 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
23607  * Based on:
23608  * Ext JS Library 1.1.1
23609  * Copyright(c) 2006-2007, Ext JS, LLC.
23610  *
23611  * Originally Released Under LGPL - original licence link has changed is not relivant.
23612  *
23613  * Fork - LGPL
23614  * <script type="text/javascript">
23615  */
23616
23617  
23618 Roo.data.Field = function(config){
23619     if(typeof config == "string"){
23620         config = {name: config};
23621     }
23622     Roo.apply(this, config);
23623     
23624     if(!this.type){
23625         this.type = "auto";
23626     }
23627     
23628     var st = Roo.data.SortTypes;
23629     // named sortTypes are supported, here we look them up
23630     if(typeof this.sortType == "string"){
23631         this.sortType = st[this.sortType];
23632     }
23633     
23634     // set default sortType for strings and dates
23635     if(!this.sortType){
23636         switch(this.type){
23637             case "string":
23638                 this.sortType = st.asUCString;
23639                 break;
23640             case "date":
23641                 this.sortType = st.asDate;
23642                 break;
23643             default:
23644                 this.sortType = st.none;
23645         }
23646     }
23647
23648     // define once
23649     var stripRe = /[\$,%]/g;
23650
23651     // prebuilt conversion function for this field, instead of
23652     // switching every time we're reading a value
23653     if(!this.convert){
23654         var cv, dateFormat = this.dateFormat;
23655         switch(this.type){
23656             case "":
23657             case "auto":
23658             case undefined:
23659                 cv = function(v){ return v; };
23660                 break;
23661             case "string":
23662                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
23663                 break;
23664             case "int":
23665                 cv = function(v){
23666                     return v !== undefined && v !== null && v !== '' ?
23667                            parseInt(String(v).replace(stripRe, ""), 10) : '';
23668                     };
23669                 break;
23670             case "float":
23671                 cv = function(v){
23672                     return v !== undefined && v !== null && v !== '' ?
23673                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
23674                     };
23675                 break;
23676             case "bool":
23677             case "boolean":
23678                 cv = function(v){ return v === true || v === "true" || v == 1; };
23679                 break;
23680             case "date":
23681                 cv = function(v){
23682                     if(!v){
23683                         return '';
23684                     }
23685                     if(v instanceof Date){
23686                         return v;
23687                     }
23688                     if(dateFormat){
23689                         if(dateFormat == "timestamp"){
23690                             return new Date(v*1000);
23691                         }
23692                         return Date.parseDate(v, dateFormat);
23693                     }
23694                     var parsed = Date.parse(v);
23695                     return parsed ? new Date(parsed) : null;
23696                 };
23697              break;
23698             
23699         }
23700         this.convert = cv;
23701     }
23702 };
23703
23704 Roo.data.Field.prototype = {
23705     dateFormat: null,
23706     defaultValue: "",
23707     mapping: null,
23708     sortType : null,
23709     sortDir : "ASC"
23710 };/*
23711  * Based on:
23712  * Ext JS Library 1.1.1
23713  * Copyright(c) 2006-2007, Ext JS, LLC.
23714  *
23715  * Originally Released Under LGPL - original licence link has changed is not relivant.
23716  *
23717  * Fork - LGPL
23718  * <script type="text/javascript">
23719  */
23720  
23721 // Base class for reading structured data from a data source.  This class is intended to be
23722 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
23723
23724 /**
23725  * @class Roo.data.DataReader
23726  * Base class for reading structured data from a data source.  This class is intended to be
23727  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
23728  */
23729
23730 Roo.data.DataReader = function(meta, recordType){
23731     
23732     this.meta = meta;
23733     
23734     this.recordType = recordType instanceof Array ? 
23735         Roo.data.Record.create(recordType) : recordType;
23736 };
23737
23738 Roo.data.DataReader.prototype = {
23739      /**
23740      * Create an empty record
23741      * @param {Object} data (optional) - overlay some values
23742      * @return {Roo.data.Record} record created.
23743      */
23744     newRow :  function(d) {
23745         var da =  {};
23746         this.recordType.prototype.fields.each(function(c) {
23747             switch( c.type) {
23748                 case 'int' : da[c.name] = 0; break;
23749                 case 'date' : da[c.name] = new Date(); break;
23750                 case 'float' : da[c.name] = 0.0; break;
23751                 case 'boolean' : da[c.name] = false; break;
23752                 default : da[c.name] = ""; break;
23753             }
23754             
23755         });
23756         return new this.recordType(Roo.apply(da, d));
23757     }
23758     
23759 };/*
23760  * Based on:
23761  * Ext JS Library 1.1.1
23762  * Copyright(c) 2006-2007, Ext JS, LLC.
23763  *
23764  * Originally Released Under LGPL - original licence link has changed is not relivant.
23765  *
23766  * Fork - LGPL
23767  * <script type="text/javascript">
23768  */
23769
23770 /**
23771  * @class Roo.data.DataProxy
23772  * @extends Roo.data.Observable
23773  * This class is an abstract base class for implementations which provide retrieval of
23774  * unformatted data objects.<br>
23775  * <p>
23776  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
23777  * (of the appropriate type which knows how to parse the data object) to provide a block of
23778  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
23779  * <p>
23780  * Custom implementations must implement the load method as described in
23781  * {@link Roo.data.HttpProxy#load}.
23782  */
23783 Roo.data.DataProxy = function(){
23784     this.addEvents({
23785         /**
23786          * @event beforeload
23787          * Fires before a network request is made to retrieve a data object.
23788          * @param {Object} This DataProxy object.
23789          * @param {Object} params The params parameter to the load function.
23790          */
23791         beforeload : true,
23792         /**
23793          * @event load
23794          * Fires before the load method's callback is called.
23795          * @param {Object} This DataProxy object.
23796          * @param {Object} o The data object.
23797          * @param {Object} arg The callback argument object passed to the load function.
23798          */
23799         load : true,
23800         /**
23801          * @event loadexception
23802          * Fires if an Exception occurs during data retrieval.
23803          * @param {Object} This DataProxy object.
23804          * @param {Object} o The data object.
23805          * @param {Object} arg The callback argument object passed to the load function.
23806          * @param {Object} e The Exception.
23807          */
23808         loadexception : true
23809     });
23810     Roo.data.DataProxy.superclass.constructor.call(this);
23811 };
23812
23813 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
23814
23815     /**
23816      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
23817      */
23818 /*
23819  * Based on:
23820  * Ext JS Library 1.1.1
23821  * Copyright(c) 2006-2007, Ext JS, LLC.
23822  *
23823  * Originally Released Under LGPL - original licence link has changed is not relivant.
23824  *
23825  * Fork - LGPL
23826  * <script type="text/javascript">
23827  */
23828 /**
23829  * @class Roo.data.MemoryProxy
23830  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
23831  * to the Reader when its load method is called.
23832  * @constructor
23833  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
23834  */
23835 Roo.data.MemoryProxy = function(data){
23836     if (data.data) {
23837         data = data.data;
23838     }
23839     Roo.data.MemoryProxy.superclass.constructor.call(this);
23840     this.data = data;
23841 };
23842
23843 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
23844     
23845     /**
23846      * Load data from the requested source (in this case an in-memory
23847      * data object passed to the constructor), read the data object into
23848      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
23849      * process that block using the passed callback.
23850      * @param {Object} params This parameter is not used by the MemoryProxy class.
23851      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23852      * object into a block of Roo.data.Records.
23853      * @param {Function} callback The function into which to pass the block of Roo.data.records.
23854      * The function must be passed <ul>
23855      * <li>The Record block object</li>
23856      * <li>The "arg" argument from the load function</li>
23857      * <li>A boolean success indicator</li>
23858      * </ul>
23859      * @param {Object} scope The scope in which to call the callback
23860      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23861      */
23862     load : function(params, reader, callback, scope, arg){
23863         params = params || {};
23864         var result;
23865         try {
23866             result = reader.readRecords(params.data ? params.data :this.data);
23867         }catch(e){
23868             this.fireEvent("loadexception", this, arg, null, e);
23869             callback.call(scope, null, arg, false);
23870             return;
23871         }
23872         callback.call(scope, result, arg, true);
23873     },
23874     
23875     // private
23876     update : function(params, records){
23877         
23878     }
23879 });/*
23880  * Based on:
23881  * Ext JS Library 1.1.1
23882  * Copyright(c) 2006-2007, Ext JS, LLC.
23883  *
23884  * Originally Released Under LGPL - original licence link has changed is not relivant.
23885  *
23886  * Fork - LGPL
23887  * <script type="text/javascript">
23888  */
23889 /**
23890  * @class Roo.data.HttpProxy
23891  * @extends Roo.data.DataProxy
23892  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
23893  * configured to reference a certain URL.<br><br>
23894  * <p>
23895  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
23896  * from which the running page was served.<br><br>
23897  * <p>
23898  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
23899  * <p>
23900  * Be aware that to enable the browser to parse an XML document, the server must set
23901  * the Content-Type header in the HTTP response to "text/xml".
23902  * @constructor
23903  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
23904  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
23905  * will be used to make the request.
23906  */
23907 Roo.data.HttpProxy = function(conn){
23908     Roo.data.HttpProxy.superclass.constructor.call(this);
23909     // is conn a conn config or a real conn?
23910     this.conn = conn;
23911     this.useAjax = !conn || !conn.events;
23912   
23913 };
23914
23915 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
23916     // thse are take from connection...
23917     
23918     /**
23919      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
23920      */
23921     /**
23922      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
23923      * extra parameters to each request made by this object. (defaults to undefined)
23924      */
23925     /**
23926      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
23927      *  to each request made by this object. (defaults to undefined)
23928      */
23929     /**
23930      * @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)
23931      */
23932     /**
23933      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
23934      */
23935      /**
23936      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
23937      * @type Boolean
23938      */
23939   
23940
23941     /**
23942      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
23943      * @type Boolean
23944      */
23945     /**
23946      * Return the {@link Roo.data.Connection} object being used by this Proxy.
23947      * @return {Connection} The Connection object. This object may be used to subscribe to events on
23948      * a finer-grained basis than the DataProxy events.
23949      */
23950     getConnection : function(){
23951         return this.useAjax ? Roo.Ajax : this.conn;
23952     },
23953
23954     /**
23955      * Load data from the configured {@link Roo.data.Connection}, read the data object into
23956      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
23957      * process that block using the passed callback.
23958      * @param {Object} params An object containing properties which are to be used as HTTP parameters
23959      * for the request to the remote server.
23960      * @param {Roo.data.DataReader} reader The Reader object which converts the data
23961      * object into a block of Roo.data.Records.
23962      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
23963      * The function must be passed <ul>
23964      * <li>The Record block object</li>
23965      * <li>The "arg" argument from the load function</li>
23966      * <li>A boolean success indicator</li>
23967      * </ul>
23968      * @param {Object} scope The scope in which to call the callback
23969      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
23970      */
23971     load : function(params, reader, callback, scope, arg){
23972         if(this.fireEvent("beforeload", this, params) !== false){
23973             var  o = {
23974                 params : params || {},
23975                 request: {
23976                     callback : callback,
23977                     scope : scope,
23978                     arg : arg
23979                 },
23980                 reader: reader,
23981                 callback : this.loadResponse,
23982                 scope: this
23983             };
23984             if(this.useAjax){
23985                 Roo.applyIf(o, this.conn);
23986                 if(this.activeRequest){
23987                     Roo.Ajax.abort(this.activeRequest);
23988                 }
23989                 this.activeRequest = Roo.Ajax.request(o);
23990             }else{
23991                 this.conn.request(o);
23992             }
23993         }else{
23994             callback.call(scope||this, null, arg, false);
23995         }
23996     },
23997
23998     // private
23999     loadResponse : function(o, success, response){
24000         delete this.activeRequest;
24001         if(!success){
24002             this.fireEvent("loadexception", this, o, response);
24003             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24004             return;
24005         }
24006         var result;
24007         try {
24008             result = o.reader.read(response);
24009         }catch(e){
24010             this.fireEvent("loadexception", this, o, response, e);
24011             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24012             return;
24013         }
24014         
24015         this.fireEvent("load", this, o, o.request.arg);
24016         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24017     },
24018
24019     // private
24020     update : function(dataSet){
24021
24022     },
24023
24024     // private
24025     updateResponse : function(dataSet){
24026
24027     }
24028 });/*
24029  * Based on:
24030  * Ext JS Library 1.1.1
24031  * Copyright(c) 2006-2007, Ext JS, LLC.
24032  *
24033  * Originally Released Under LGPL - original licence link has changed is not relivant.
24034  *
24035  * Fork - LGPL
24036  * <script type="text/javascript">
24037  */
24038
24039 /**
24040  * @class Roo.data.ScriptTagProxy
24041  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24042  * other than the originating domain of the running page.<br><br>
24043  * <p>
24044  * <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
24045  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24046  * <p>
24047  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24048  * source code that is used as the source inside a &lt;script> tag.<br><br>
24049  * <p>
24050  * In order for the browser to process the returned data, the server must wrap the data object
24051  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24052  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24053  * depending on whether the callback name was passed:
24054  * <p>
24055  * <pre><code>
24056 boolean scriptTag = false;
24057 String cb = request.getParameter("callback");
24058 if (cb != null) {
24059     scriptTag = true;
24060     response.setContentType("text/javascript");
24061 } else {
24062     response.setContentType("application/x-json");
24063 }
24064 Writer out = response.getWriter();
24065 if (scriptTag) {
24066     out.write(cb + "(");
24067 }
24068 out.print(dataBlock.toJsonString());
24069 if (scriptTag) {
24070     out.write(");");
24071 }
24072 </pre></code>
24073  *
24074  * @constructor
24075  * @param {Object} config A configuration object.
24076  */
24077 Roo.data.ScriptTagProxy = function(config){
24078     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24079     Roo.apply(this, config);
24080     this.head = document.getElementsByTagName("head")[0];
24081 };
24082
24083 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24084
24085 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24086     /**
24087      * @cfg {String} url The URL from which to request the data object.
24088      */
24089     /**
24090      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24091      */
24092     timeout : 30000,
24093     /**
24094      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24095      * the server the name of the callback function set up by the load call to process the returned data object.
24096      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24097      * javascript output which calls this named function passing the data object as its only parameter.
24098      */
24099     callbackParam : "callback",
24100     /**
24101      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24102      * name to the request.
24103      */
24104     nocache : true,
24105
24106     /**
24107      * Load data from the configured URL, read the data object into
24108      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24109      * process that block using the passed callback.
24110      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24111      * for the request to the remote server.
24112      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24113      * object into a block of Roo.data.Records.
24114      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24115      * The function must be passed <ul>
24116      * <li>The Record block object</li>
24117      * <li>The "arg" argument from the load function</li>
24118      * <li>A boolean success indicator</li>
24119      * </ul>
24120      * @param {Object} scope The scope in which to call the callback
24121      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24122      */
24123     load : function(params, reader, callback, scope, arg){
24124         if(this.fireEvent("beforeload", this, params) !== false){
24125
24126             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24127
24128             var url = this.url;
24129             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24130             if(this.nocache){
24131                 url += "&_dc=" + (new Date().getTime());
24132             }
24133             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24134             var trans = {
24135                 id : transId,
24136                 cb : "stcCallback"+transId,
24137                 scriptId : "stcScript"+transId,
24138                 params : params,
24139                 arg : arg,
24140                 url : url,
24141                 callback : callback,
24142                 scope : scope,
24143                 reader : reader
24144             };
24145             var conn = this;
24146
24147             window[trans.cb] = function(o){
24148                 conn.handleResponse(o, trans);
24149             };
24150
24151             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24152
24153             if(this.autoAbort !== false){
24154                 this.abort();
24155             }
24156
24157             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24158
24159             var script = document.createElement("script");
24160             script.setAttribute("src", url);
24161             script.setAttribute("type", "text/javascript");
24162             script.setAttribute("id", trans.scriptId);
24163             this.head.appendChild(script);
24164
24165             this.trans = trans;
24166         }else{
24167             callback.call(scope||this, null, arg, false);
24168         }
24169     },
24170
24171     // private
24172     isLoading : function(){
24173         return this.trans ? true : false;
24174     },
24175
24176     /**
24177      * Abort the current server request.
24178      */
24179     abort : function(){
24180         if(this.isLoading()){
24181             this.destroyTrans(this.trans);
24182         }
24183     },
24184
24185     // private
24186     destroyTrans : function(trans, isLoaded){
24187         this.head.removeChild(document.getElementById(trans.scriptId));
24188         clearTimeout(trans.timeoutId);
24189         if(isLoaded){
24190             window[trans.cb] = undefined;
24191             try{
24192                 delete window[trans.cb];
24193             }catch(e){}
24194         }else{
24195             // if hasn't been loaded, wait for load to remove it to prevent script error
24196             window[trans.cb] = function(){
24197                 window[trans.cb] = undefined;
24198                 try{
24199                     delete window[trans.cb];
24200                 }catch(e){}
24201             };
24202         }
24203     },
24204
24205     // private
24206     handleResponse : function(o, trans){
24207         this.trans = false;
24208         this.destroyTrans(trans, true);
24209         var result;
24210         try {
24211             result = trans.reader.readRecords(o);
24212         }catch(e){
24213             this.fireEvent("loadexception", this, o, trans.arg, e);
24214             trans.callback.call(trans.scope||window, null, trans.arg, false);
24215             return;
24216         }
24217         this.fireEvent("load", this, o, trans.arg);
24218         trans.callback.call(trans.scope||window, result, trans.arg, true);
24219     },
24220
24221     // private
24222     handleFailure : function(trans){
24223         this.trans = false;
24224         this.destroyTrans(trans, false);
24225         this.fireEvent("loadexception", this, null, trans.arg);
24226         trans.callback.call(trans.scope||window, null, trans.arg, false);
24227     }
24228 });/*
24229  * Based on:
24230  * Ext JS Library 1.1.1
24231  * Copyright(c) 2006-2007, Ext JS, LLC.
24232  *
24233  * Originally Released Under LGPL - original licence link has changed is not relivant.
24234  *
24235  * Fork - LGPL
24236  * <script type="text/javascript">
24237  */
24238
24239 /**
24240  * @class Roo.data.JsonReader
24241  * @extends Roo.data.DataReader
24242  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24243  * based on mappings in a provided Roo.data.Record constructor.
24244  * 
24245  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24246  * in the reply previously. 
24247  * 
24248  * <p>
24249  * Example code:
24250  * <pre><code>
24251 var RecordDef = Roo.data.Record.create([
24252     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24253     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24254 ]);
24255 var myReader = new Roo.data.JsonReader({
24256     totalProperty: "results",    // The property which contains the total dataset size (optional)
24257     root: "rows",                // The property which contains an Array of row objects
24258     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24259 }, RecordDef);
24260 </code></pre>
24261  * <p>
24262  * This would consume a JSON file like this:
24263  * <pre><code>
24264 { 'results': 2, 'rows': [
24265     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24266     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24267 }
24268 </code></pre>
24269  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24270  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24271  * paged from the remote server.
24272  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24273  * @cfg {String} root name of the property which contains the Array of row objects.
24274  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24275  * @cfg {Array} fields Array of field definition objects
24276  * @constructor
24277  * Create a new JsonReader
24278  * @param {Object} meta Metadata configuration options
24279  * @param {Object} recordType Either an Array of field definition objects,
24280  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24281  */
24282 Roo.data.JsonReader = function(meta, recordType){
24283     
24284     meta = meta || {};
24285     // set some defaults:
24286     Roo.applyIf(meta, {
24287         totalProperty: 'total',
24288         successProperty : 'success',
24289         root : 'data',
24290         id : 'id'
24291     });
24292     
24293     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24294 };
24295 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24296     
24297     /**
24298      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24299      * Used by Store query builder to append _requestMeta to params.
24300      * 
24301      */
24302     metaFromRemote : false,
24303     /**
24304      * This method is only used by a DataProxy which has retrieved data from a remote server.
24305      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24306      * @return {Object} data A data block which is used by an Roo.data.Store object as
24307      * a cache of Roo.data.Records.
24308      */
24309     read : function(response){
24310         var json = response.responseText;
24311        
24312         var o = /* eval:var:o */ eval("("+json+")");
24313         if(!o) {
24314             throw {message: "JsonReader.read: Json object not found"};
24315         }
24316         
24317         if(o.metaData){
24318             
24319             delete this.ef;
24320             this.metaFromRemote = true;
24321             this.meta = o.metaData;
24322             this.recordType = Roo.data.Record.create(o.metaData.fields);
24323             this.onMetaChange(this.meta, this.recordType, o);
24324         }
24325         return this.readRecords(o);
24326     },
24327
24328     // private function a store will implement
24329     onMetaChange : function(meta, recordType, o){
24330
24331     },
24332
24333     /**
24334          * @ignore
24335          */
24336     simpleAccess: function(obj, subsc) {
24337         return obj[subsc];
24338     },
24339
24340         /**
24341          * @ignore
24342          */
24343     getJsonAccessor: function(){
24344         var re = /[\[\.]/;
24345         return function(expr) {
24346             try {
24347                 return(re.test(expr))
24348                     ? new Function("obj", "return obj." + expr)
24349                     : function(obj){
24350                         return obj[expr];
24351                     };
24352             } catch(e){}
24353             return Roo.emptyFn;
24354         };
24355     }(),
24356
24357     /**
24358      * Create a data block containing Roo.data.Records from an XML document.
24359      * @param {Object} o An object which contains an Array of row objects in the property specified
24360      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
24361      * which contains the total size of the dataset.
24362      * @return {Object} data A data block which is used by an Roo.data.Store object as
24363      * a cache of Roo.data.Records.
24364      */
24365     readRecords : function(o){
24366         /**
24367          * After any data loads, the raw JSON data is available for further custom processing.
24368          * @type Object
24369          */
24370         this.o = o;
24371         var s = this.meta, Record = this.recordType,
24372             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
24373
24374 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
24375         if (!this.ef) {
24376             if(s.totalProperty) {
24377                     this.getTotal = this.getJsonAccessor(s.totalProperty);
24378                 }
24379                 if(s.successProperty) {
24380                     this.getSuccess = this.getJsonAccessor(s.successProperty);
24381                 }
24382                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
24383                 if (s.id) {
24384                         var g = this.getJsonAccessor(s.id);
24385                         this.getId = function(rec) {
24386                                 var r = g(rec);  
24387                                 return (r === undefined || r === "") ? null : r;
24388                         };
24389                 } else {
24390                         this.getId = function(){return null;};
24391                 }
24392             this.ef = [];
24393             for(var jj = 0; jj < fl; jj++){
24394                 f = fi[jj];
24395                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
24396                 this.ef[jj] = this.getJsonAccessor(map);
24397             }
24398         }
24399
24400         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
24401         if(s.totalProperty){
24402             var vt = parseInt(this.getTotal(o), 10);
24403             if(!isNaN(vt)){
24404                 totalRecords = vt;
24405             }
24406         }
24407         if(s.successProperty){
24408             var vs = this.getSuccess(o);
24409             if(vs === false || vs === 'false'){
24410                 success = false;
24411             }
24412         }
24413         var records = [];
24414         for(var i = 0; i < c; i++){
24415                 var n = root[i];
24416             var values = {};
24417             var id = this.getId(n);
24418             for(var j = 0; j < fl; j++){
24419                 f = fi[j];
24420             var v = this.ef[j](n);
24421             if (!f.convert) {
24422                 Roo.log('missing convert for ' + f.name);
24423                 Roo.log(f);
24424                 continue;
24425             }
24426             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
24427             }
24428             var record = new Record(values, id);
24429             record.json = n;
24430             records[i] = record;
24431         }
24432         return {
24433             raw : o,
24434             success : success,
24435             records : records,
24436             totalRecords : totalRecords
24437         };
24438     }
24439 });/*
24440  * Based on:
24441  * Ext JS Library 1.1.1
24442  * Copyright(c) 2006-2007, Ext JS, LLC.
24443  *
24444  * Originally Released Under LGPL - original licence link has changed is not relivant.
24445  *
24446  * Fork - LGPL
24447  * <script type="text/javascript">
24448  */
24449
24450 /**
24451  * @class Roo.data.XmlReader
24452  * @extends Roo.data.DataReader
24453  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
24454  * based on mappings in a provided Roo.data.Record constructor.<br><br>
24455  * <p>
24456  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
24457  * header in the HTTP response must be set to "text/xml".</em>
24458  * <p>
24459  * Example code:
24460  * <pre><code>
24461 var RecordDef = Roo.data.Record.create([
24462    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24463    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24464 ]);
24465 var myReader = new Roo.data.XmlReader({
24466    totalRecords: "results", // The element which contains the total dataset size (optional)
24467    record: "row",           // The repeated element which contains row information
24468    id: "id"                 // The element within the row that provides an ID for the record (optional)
24469 }, RecordDef);
24470 </code></pre>
24471  * <p>
24472  * This would consume an XML file like this:
24473  * <pre><code>
24474 &lt;?xml?>
24475 &lt;dataset>
24476  &lt;results>2&lt;/results>
24477  &lt;row>
24478    &lt;id>1&lt;/id>
24479    &lt;name>Bill&lt;/name>
24480    &lt;occupation>Gardener&lt;/occupation>
24481  &lt;/row>
24482  &lt;row>
24483    &lt;id>2&lt;/id>
24484    &lt;name>Ben&lt;/name>
24485    &lt;occupation>Horticulturalist&lt;/occupation>
24486  &lt;/row>
24487 &lt;/dataset>
24488 </code></pre>
24489  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
24490  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24491  * paged from the remote server.
24492  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
24493  * @cfg {String} success The DomQuery path to the success attribute used by forms.
24494  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
24495  * a record identifier value.
24496  * @constructor
24497  * Create a new XmlReader
24498  * @param {Object} meta Metadata configuration options
24499  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
24500  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
24501  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
24502  */
24503 Roo.data.XmlReader = function(meta, recordType){
24504     meta = meta || {};
24505     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24506 };
24507 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
24508     /**
24509      * This method is only used by a DataProxy which has retrieved data from a remote server.
24510          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
24511          * to contain a method called 'responseXML' that returns an XML document object.
24512      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24513      * a cache of Roo.data.Records.
24514      */
24515     read : function(response){
24516         var doc = response.responseXML;
24517         if(!doc) {
24518             throw {message: "XmlReader.read: XML Document not available"};
24519         }
24520         return this.readRecords(doc);
24521     },
24522
24523     /**
24524      * Create a data block containing Roo.data.Records from an XML document.
24525          * @param {Object} doc A parsed XML document.
24526      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
24527      * a cache of Roo.data.Records.
24528      */
24529     readRecords : function(doc){
24530         /**
24531          * After any data loads/reads, the raw XML Document is available for further custom processing.
24532          * @type XMLDocument
24533          */
24534         this.xmlData = doc;
24535         var root = doc.documentElement || doc;
24536         var q = Roo.DomQuery;
24537         var recordType = this.recordType, fields = recordType.prototype.fields;
24538         var sid = this.meta.id;
24539         var totalRecords = 0, success = true;
24540         if(this.meta.totalRecords){
24541             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
24542         }
24543         
24544         if(this.meta.success){
24545             var sv = q.selectValue(this.meta.success, root, true);
24546             success = sv !== false && sv !== 'false';
24547         }
24548         var records = [];
24549         var ns = q.select(this.meta.record, root);
24550         for(var i = 0, len = ns.length; i < len; i++) {
24551                 var n = ns[i];
24552                 var values = {};
24553                 var id = sid ? q.selectValue(sid, n) : undefined;
24554                 for(var j = 0, jlen = fields.length; j < jlen; j++){
24555                     var f = fields.items[j];
24556                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
24557                     v = f.convert(v);
24558                     values[f.name] = v;
24559                 }
24560                 var record = new recordType(values, id);
24561                 record.node = n;
24562                 records[records.length] = record;
24563             }
24564
24565             return {
24566                 success : success,
24567                 records : records,
24568                 totalRecords : totalRecords || records.length
24569             };
24570     }
24571 });/*
24572  * Based on:
24573  * Ext JS Library 1.1.1
24574  * Copyright(c) 2006-2007, Ext JS, LLC.
24575  *
24576  * Originally Released Under LGPL - original licence link has changed is not relivant.
24577  *
24578  * Fork - LGPL
24579  * <script type="text/javascript">
24580  */
24581
24582 /**
24583  * @class Roo.data.ArrayReader
24584  * @extends Roo.data.DataReader
24585  * Data reader class to create an Array of Roo.data.Record objects from an Array.
24586  * Each element of that Array represents a row of data fields. The
24587  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
24588  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
24589  * <p>
24590  * Example code:.
24591  * <pre><code>
24592 var RecordDef = Roo.data.Record.create([
24593     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
24594     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
24595 ]);
24596 var myReader = new Roo.data.ArrayReader({
24597     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
24598 }, RecordDef);
24599 </code></pre>
24600  * <p>
24601  * This would consume an Array like this:
24602  * <pre><code>
24603 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
24604   </code></pre>
24605  
24606  * @constructor
24607  * Create a new JsonReader
24608  * @param {Object} meta Metadata configuration options.
24609  * @param {Object|Array} recordType Either an Array of field definition objects
24610  * 
24611  * @cfg {Array} fields Array of field definition objects
24612  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24613  * as specified to {@link Roo.data.Record#create},
24614  * or an {@link Roo.data.Record} object
24615  *
24616  * 
24617  * created using {@link Roo.data.Record#create}.
24618  */
24619 Roo.data.ArrayReader = function(meta, recordType){
24620     
24621      
24622     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24623 };
24624
24625 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
24626     /**
24627      * Create a data block containing Roo.data.Records from an XML document.
24628      * @param {Object} o An Array of row objects which represents the dataset.
24629      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
24630      * a cache of Roo.data.Records.
24631      */
24632     readRecords : function(o)
24633     {
24634         var sid = this.meta ? this.meta.id : null;
24635         var recordType = this.recordType, fields = recordType.prototype.fields;
24636         var records = [];
24637         var root = o;
24638         for(var i = 0; i < root.length; i++){
24639                 var n = root[i];
24640             var values = {};
24641             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
24642             for(var j = 0, jlen = fields.length; j < jlen; j++){
24643                 var f = fields.items[j];
24644                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
24645                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
24646                 v = f.convert(v);
24647                 values[f.name] = v;
24648             }
24649             var record = new recordType(values, id);
24650             record.json = n;
24651             records[records.length] = record;
24652         }
24653         return {
24654             records : records,
24655             totalRecords : records.length
24656         };
24657     }
24658 });/*
24659  * Based on:
24660  * Ext JS Library 1.1.1
24661  * Copyright(c) 2006-2007, Ext JS, LLC.
24662  *
24663  * Originally Released Under LGPL - original licence link has changed is not relivant.
24664  *
24665  * Fork - LGPL
24666  * <script type="text/javascript">
24667  */
24668
24669
24670 /**
24671  * @class Roo.data.Tree
24672  * @extends Roo.util.Observable
24673  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
24674  * in the tree have most standard DOM functionality.
24675  * @constructor
24676  * @param {Node} root (optional) The root node
24677  */
24678 Roo.data.Tree = function(root){
24679    this.nodeHash = {};
24680    /**
24681     * The root node for this tree
24682     * @type Node
24683     */
24684    this.root = null;
24685    if(root){
24686        this.setRootNode(root);
24687    }
24688    this.addEvents({
24689        /**
24690         * @event append
24691         * Fires when a new child node is appended to a node in this tree.
24692         * @param {Tree} tree The owner tree
24693         * @param {Node} parent The parent node
24694         * @param {Node} node The newly appended node
24695         * @param {Number} index The index of the newly appended node
24696         */
24697        "append" : true,
24698        /**
24699         * @event remove
24700         * Fires when a child node is removed from a node in this tree.
24701         * @param {Tree} tree The owner tree
24702         * @param {Node} parent The parent node
24703         * @param {Node} node The child node removed
24704         */
24705        "remove" : true,
24706        /**
24707         * @event move
24708         * Fires when a node is moved to a new location in the tree
24709         * @param {Tree} tree The owner tree
24710         * @param {Node} node The node moved
24711         * @param {Node} oldParent The old parent of this node
24712         * @param {Node} newParent The new parent of this node
24713         * @param {Number} index The index it was moved to
24714         */
24715        "move" : true,
24716        /**
24717         * @event insert
24718         * Fires when a new child node is inserted in a node in this tree.
24719         * @param {Tree} tree The owner tree
24720         * @param {Node} parent The parent node
24721         * @param {Node} node The child node inserted
24722         * @param {Node} refNode The child node the node was inserted before
24723         */
24724        "insert" : true,
24725        /**
24726         * @event beforeappend
24727         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
24728         * @param {Tree} tree The owner tree
24729         * @param {Node} parent The parent node
24730         * @param {Node} node The child node to be appended
24731         */
24732        "beforeappend" : true,
24733        /**
24734         * @event beforeremove
24735         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
24736         * @param {Tree} tree The owner tree
24737         * @param {Node} parent The parent node
24738         * @param {Node} node The child node to be removed
24739         */
24740        "beforeremove" : true,
24741        /**
24742         * @event beforemove
24743         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
24744         * @param {Tree} tree The owner tree
24745         * @param {Node} node The node being moved
24746         * @param {Node} oldParent The parent of the node
24747         * @param {Node} newParent The new parent the node is moving to
24748         * @param {Number} index The index it is being moved to
24749         */
24750        "beforemove" : true,
24751        /**
24752         * @event beforeinsert
24753         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
24754         * @param {Tree} tree The owner tree
24755         * @param {Node} parent The parent node
24756         * @param {Node} node The child node to be inserted
24757         * @param {Node} refNode The child node the node is being inserted before
24758         */
24759        "beforeinsert" : true
24760    });
24761
24762     Roo.data.Tree.superclass.constructor.call(this);
24763 };
24764
24765 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
24766     pathSeparator: "/",
24767
24768     proxyNodeEvent : function(){
24769         return this.fireEvent.apply(this, arguments);
24770     },
24771
24772     /**
24773      * Returns the root node for this tree.
24774      * @return {Node}
24775      */
24776     getRootNode : function(){
24777         return this.root;
24778     },
24779
24780     /**
24781      * Sets the root node for this tree.
24782      * @param {Node} node
24783      * @return {Node}
24784      */
24785     setRootNode : function(node){
24786         this.root = node;
24787         node.ownerTree = this;
24788         node.isRoot = true;
24789         this.registerNode(node);
24790         return node;
24791     },
24792
24793     /**
24794      * Gets a node in this tree by its id.
24795      * @param {String} id
24796      * @return {Node}
24797      */
24798     getNodeById : function(id){
24799         return this.nodeHash[id];
24800     },
24801
24802     registerNode : function(node){
24803         this.nodeHash[node.id] = node;
24804     },
24805
24806     unregisterNode : function(node){
24807         delete this.nodeHash[node.id];
24808     },
24809
24810     toString : function(){
24811         return "[Tree"+(this.id?" "+this.id:"")+"]";
24812     }
24813 });
24814
24815 /**
24816  * @class Roo.data.Node
24817  * @extends Roo.util.Observable
24818  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
24819  * @cfg {String} id The id for this node. If one is not specified, one is generated.
24820  * @constructor
24821  * @param {Object} attributes The attributes/config for the node
24822  */
24823 Roo.data.Node = function(attributes){
24824     /**
24825      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
24826      * @type {Object}
24827      */
24828     this.attributes = attributes || {};
24829     this.leaf = this.attributes.leaf;
24830     /**
24831      * The node id. @type String
24832      */
24833     this.id = this.attributes.id;
24834     if(!this.id){
24835         this.id = Roo.id(null, "ynode-");
24836         this.attributes.id = this.id;
24837     }
24838      
24839     
24840     /**
24841      * All child nodes of this node. @type Array
24842      */
24843     this.childNodes = [];
24844     if(!this.childNodes.indexOf){ // indexOf is a must
24845         this.childNodes.indexOf = function(o){
24846             for(var i = 0, len = this.length; i < len; i++){
24847                 if(this[i] == o) {
24848                     return i;
24849                 }
24850             }
24851             return -1;
24852         };
24853     }
24854     /**
24855      * The parent node for this node. @type Node
24856      */
24857     this.parentNode = null;
24858     /**
24859      * The first direct child node of this node, or null if this node has no child nodes. @type Node
24860      */
24861     this.firstChild = null;
24862     /**
24863      * The last direct child node of this node, or null if this node has no child nodes. @type Node
24864      */
24865     this.lastChild = null;
24866     /**
24867      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
24868      */
24869     this.previousSibling = null;
24870     /**
24871      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
24872      */
24873     this.nextSibling = null;
24874
24875     this.addEvents({
24876        /**
24877         * @event append
24878         * Fires when a new child node is appended
24879         * @param {Tree} tree The owner tree
24880         * @param {Node} this This node
24881         * @param {Node} node The newly appended node
24882         * @param {Number} index The index of the newly appended node
24883         */
24884        "append" : true,
24885        /**
24886         * @event remove
24887         * Fires when a child node is removed
24888         * @param {Tree} tree The owner tree
24889         * @param {Node} this This node
24890         * @param {Node} node The removed node
24891         */
24892        "remove" : true,
24893        /**
24894         * @event move
24895         * Fires when this node is moved to a new location in the tree
24896         * @param {Tree} tree The owner tree
24897         * @param {Node} this This node
24898         * @param {Node} oldParent The old parent of this node
24899         * @param {Node} newParent The new parent of this node
24900         * @param {Number} index The index it was moved to
24901         */
24902        "move" : true,
24903        /**
24904         * @event insert
24905         * Fires when a new child node is inserted.
24906         * @param {Tree} tree The owner tree
24907         * @param {Node} this This node
24908         * @param {Node} node The child node inserted
24909         * @param {Node} refNode The child node the node was inserted before
24910         */
24911        "insert" : true,
24912        /**
24913         * @event beforeappend
24914         * Fires before a new child is appended, return false to cancel the append.
24915         * @param {Tree} tree The owner tree
24916         * @param {Node} this This node
24917         * @param {Node} node The child node to be appended
24918         */
24919        "beforeappend" : true,
24920        /**
24921         * @event beforeremove
24922         * Fires before a child is removed, return false to cancel the remove.
24923         * @param {Tree} tree The owner tree
24924         * @param {Node} this This node
24925         * @param {Node} node The child node to be removed
24926         */
24927        "beforeremove" : true,
24928        /**
24929         * @event beforemove
24930         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
24931         * @param {Tree} tree The owner tree
24932         * @param {Node} this This node
24933         * @param {Node} oldParent The parent of this node
24934         * @param {Node} newParent The new parent this node is moving to
24935         * @param {Number} index The index it is being moved to
24936         */
24937        "beforemove" : true,
24938        /**
24939         * @event beforeinsert
24940         * Fires before a new child is inserted, return false to cancel the insert.
24941         * @param {Tree} tree The owner tree
24942         * @param {Node} this This node
24943         * @param {Node} node The child node to be inserted
24944         * @param {Node} refNode The child node the node is being inserted before
24945         */
24946        "beforeinsert" : true
24947    });
24948     this.listeners = this.attributes.listeners;
24949     Roo.data.Node.superclass.constructor.call(this);
24950 };
24951
24952 Roo.extend(Roo.data.Node, Roo.util.Observable, {
24953     fireEvent : function(evtName){
24954         // first do standard event for this node
24955         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
24956             return false;
24957         }
24958         // then bubble it up to the tree if the event wasn't cancelled
24959         var ot = this.getOwnerTree();
24960         if(ot){
24961             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
24962                 return false;
24963             }
24964         }
24965         return true;
24966     },
24967
24968     /**
24969      * Returns true if this node is a leaf
24970      * @return {Boolean}
24971      */
24972     isLeaf : function(){
24973         return this.leaf === true;
24974     },
24975
24976     // private
24977     setFirstChild : function(node){
24978         this.firstChild = node;
24979     },
24980
24981     //private
24982     setLastChild : function(node){
24983         this.lastChild = node;
24984     },
24985
24986
24987     /**
24988      * Returns true if this node is the last child of its parent
24989      * @return {Boolean}
24990      */
24991     isLast : function(){
24992        return (!this.parentNode ? true : this.parentNode.lastChild == this);
24993     },
24994
24995     /**
24996      * Returns true if this node is the first child of its parent
24997      * @return {Boolean}
24998      */
24999     isFirst : function(){
25000        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25001     },
25002
25003     hasChildNodes : function(){
25004         return !this.isLeaf() && this.childNodes.length > 0;
25005     },
25006
25007     /**
25008      * Insert node(s) as the last child node of this node.
25009      * @param {Node/Array} node The node or Array of nodes to append
25010      * @return {Node} The appended node if single append, or null if an array was passed
25011      */
25012     appendChild : function(node){
25013         var multi = false;
25014         if(node instanceof Array){
25015             multi = node;
25016         }else if(arguments.length > 1){
25017             multi = arguments;
25018         }
25019         
25020         // if passed an array or multiple args do them one by one
25021         if(multi){
25022             for(var i = 0, len = multi.length; i < len; i++) {
25023                 this.appendChild(multi[i]);
25024             }
25025         }else{
25026             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25027                 return false;
25028             }
25029             var index = this.childNodes.length;
25030             var oldParent = node.parentNode;
25031             // it's a move, make sure we move it cleanly
25032             if(oldParent){
25033                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25034                     return false;
25035                 }
25036                 oldParent.removeChild(node);
25037             }
25038             
25039             index = this.childNodes.length;
25040             if(index == 0){
25041                 this.setFirstChild(node);
25042             }
25043             this.childNodes.push(node);
25044             node.parentNode = this;
25045             var ps = this.childNodes[index-1];
25046             if(ps){
25047                 node.previousSibling = ps;
25048                 ps.nextSibling = node;
25049             }else{
25050                 node.previousSibling = null;
25051             }
25052             node.nextSibling = null;
25053             this.setLastChild(node);
25054             node.setOwnerTree(this.getOwnerTree());
25055             this.fireEvent("append", this.ownerTree, this, node, index);
25056             if(this.ownerTree) {
25057                 this.ownerTree.fireEvent("appendnode", this, node, index);
25058             }
25059             if(oldParent){
25060                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25061             }
25062             return node;
25063         }
25064     },
25065
25066     /**
25067      * Removes a child node from this node.
25068      * @param {Node} node The node to remove
25069      * @return {Node} The removed node
25070      */
25071     removeChild : function(node){
25072         var index = this.childNodes.indexOf(node);
25073         if(index == -1){
25074             return false;
25075         }
25076         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25077             return false;
25078         }
25079
25080         // remove it from childNodes collection
25081         this.childNodes.splice(index, 1);
25082
25083         // update siblings
25084         if(node.previousSibling){
25085             node.previousSibling.nextSibling = node.nextSibling;
25086         }
25087         if(node.nextSibling){
25088             node.nextSibling.previousSibling = node.previousSibling;
25089         }
25090
25091         // update child refs
25092         if(this.firstChild == node){
25093             this.setFirstChild(node.nextSibling);
25094         }
25095         if(this.lastChild == node){
25096             this.setLastChild(node.previousSibling);
25097         }
25098
25099         node.setOwnerTree(null);
25100         // clear any references from the node
25101         node.parentNode = null;
25102         node.previousSibling = null;
25103         node.nextSibling = null;
25104         this.fireEvent("remove", this.ownerTree, this, node);
25105         return node;
25106     },
25107
25108     /**
25109      * Inserts the first node before the second node in this nodes childNodes collection.
25110      * @param {Node} node The node to insert
25111      * @param {Node} refNode The node to insert before (if null the node is appended)
25112      * @return {Node} The inserted node
25113      */
25114     insertBefore : function(node, refNode){
25115         if(!refNode){ // like standard Dom, refNode can be null for append
25116             return this.appendChild(node);
25117         }
25118         // nothing to do
25119         if(node == refNode){
25120             return false;
25121         }
25122
25123         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25124             return false;
25125         }
25126         var index = this.childNodes.indexOf(refNode);
25127         var oldParent = node.parentNode;
25128         var refIndex = index;
25129
25130         // when moving internally, indexes will change after remove
25131         if(oldParent == this && this.childNodes.indexOf(node) < index){
25132             refIndex--;
25133         }
25134
25135         // it's a move, make sure we move it cleanly
25136         if(oldParent){
25137             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25138                 return false;
25139             }
25140             oldParent.removeChild(node);
25141         }
25142         if(refIndex == 0){
25143             this.setFirstChild(node);
25144         }
25145         this.childNodes.splice(refIndex, 0, node);
25146         node.parentNode = this;
25147         var ps = this.childNodes[refIndex-1];
25148         if(ps){
25149             node.previousSibling = ps;
25150             ps.nextSibling = node;
25151         }else{
25152             node.previousSibling = null;
25153         }
25154         node.nextSibling = refNode;
25155         refNode.previousSibling = node;
25156         node.setOwnerTree(this.getOwnerTree());
25157         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25158         if(oldParent){
25159             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25160         }
25161         return node;
25162     },
25163
25164     /**
25165      * Returns the child node at the specified index.
25166      * @param {Number} index
25167      * @return {Node}
25168      */
25169     item : function(index){
25170         return this.childNodes[index];
25171     },
25172
25173     /**
25174      * Replaces one child node in this node with another.
25175      * @param {Node} newChild The replacement node
25176      * @param {Node} oldChild The node to replace
25177      * @return {Node} The replaced node
25178      */
25179     replaceChild : function(newChild, oldChild){
25180         this.insertBefore(newChild, oldChild);
25181         this.removeChild(oldChild);
25182         return oldChild;
25183     },
25184
25185     /**
25186      * Returns the index of a child node
25187      * @param {Node} node
25188      * @return {Number} The index of the node or -1 if it was not found
25189      */
25190     indexOf : function(child){
25191         return this.childNodes.indexOf(child);
25192     },
25193
25194     /**
25195      * Returns the tree this node is in.
25196      * @return {Tree}
25197      */
25198     getOwnerTree : function(){
25199         // if it doesn't have one, look for one
25200         if(!this.ownerTree){
25201             var p = this;
25202             while(p){
25203                 if(p.ownerTree){
25204                     this.ownerTree = p.ownerTree;
25205                     break;
25206                 }
25207                 p = p.parentNode;
25208             }
25209         }
25210         return this.ownerTree;
25211     },
25212
25213     /**
25214      * Returns depth of this node (the root node has a depth of 0)
25215      * @return {Number}
25216      */
25217     getDepth : function(){
25218         var depth = 0;
25219         var p = this;
25220         while(p.parentNode){
25221             ++depth;
25222             p = p.parentNode;
25223         }
25224         return depth;
25225     },
25226
25227     // private
25228     setOwnerTree : function(tree){
25229         // if it's move, we need to update everyone
25230         if(tree != this.ownerTree){
25231             if(this.ownerTree){
25232                 this.ownerTree.unregisterNode(this);
25233             }
25234             this.ownerTree = tree;
25235             var cs = this.childNodes;
25236             for(var i = 0, len = cs.length; i < len; i++) {
25237                 cs[i].setOwnerTree(tree);
25238             }
25239             if(tree){
25240                 tree.registerNode(this);
25241             }
25242         }
25243     },
25244
25245     /**
25246      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25247      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25248      * @return {String} The path
25249      */
25250     getPath : function(attr){
25251         attr = attr || "id";
25252         var p = this.parentNode;
25253         var b = [this.attributes[attr]];
25254         while(p){
25255             b.unshift(p.attributes[attr]);
25256             p = p.parentNode;
25257         }
25258         var sep = this.getOwnerTree().pathSeparator;
25259         return sep + b.join(sep);
25260     },
25261
25262     /**
25263      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25264      * function call will be the scope provided or the current node. The arguments to the function
25265      * will be the args provided or the current node. If the function returns false at any point,
25266      * the bubble is stopped.
25267      * @param {Function} fn The function to call
25268      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25269      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25270      */
25271     bubble : function(fn, scope, args){
25272         var p = this;
25273         while(p){
25274             if(fn.call(scope || p, args || p) === false){
25275                 break;
25276             }
25277             p = p.parentNode;
25278         }
25279     },
25280
25281     /**
25282      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25283      * function call will be the scope provided or the current node. The arguments to the function
25284      * will be the args provided or the current node. If the function returns false at any point,
25285      * the cascade is stopped on that branch.
25286      * @param {Function} fn The function to call
25287      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25288      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25289      */
25290     cascade : function(fn, scope, args){
25291         if(fn.call(scope || this, args || this) !== false){
25292             var cs = this.childNodes;
25293             for(var i = 0, len = cs.length; i < len; i++) {
25294                 cs[i].cascade(fn, scope, args);
25295             }
25296         }
25297     },
25298
25299     /**
25300      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25301      * function call will be the scope provided or the current node. The arguments to the function
25302      * will be the args provided or the current node. If the function returns false at any point,
25303      * the iteration stops.
25304      * @param {Function} fn The function to call
25305      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25306      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25307      */
25308     eachChild : function(fn, scope, args){
25309         var cs = this.childNodes;
25310         for(var i = 0, len = cs.length; i < len; i++) {
25311                 if(fn.call(scope || this, args || cs[i]) === false){
25312                     break;
25313                 }
25314         }
25315     },
25316
25317     /**
25318      * Finds the first child that has the attribute with the specified value.
25319      * @param {String} attribute The attribute name
25320      * @param {Mixed} value The value to search for
25321      * @return {Node} The found child or null if none was found
25322      */
25323     findChild : function(attribute, value){
25324         var cs = this.childNodes;
25325         for(var i = 0, len = cs.length; i < len; i++) {
25326                 if(cs[i].attributes[attribute] == value){
25327                     return cs[i];
25328                 }
25329         }
25330         return null;
25331     },
25332
25333     /**
25334      * Finds the first child by a custom function. The child matches if the function passed
25335      * returns true.
25336      * @param {Function} fn
25337      * @param {Object} scope (optional)
25338      * @return {Node} The found child or null if none was found
25339      */
25340     findChildBy : function(fn, scope){
25341         var cs = this.childNodes;
25342         for(var i = 0, len = cs.length; i < len; i++) {
25343                 if(fn.call(scope||cs[i], cs[i]) === true){
25344                     return cs[i];
25345                 }
25346         }
25347         return null;
25348     },
25349
25350     /**
25351      * Sorts this nodes children using the supplied sort function
25352      * @param {Function} fn
25353      * @param {Object} scope (optional)
25354      */
25355     sort : function(fn, scope){
25356         var cs = this.childNodes;
25357         var len = cs.length;
25358         if(len > 0){
25359             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
25360             cs.sort(sortFn);
25361             for(var i = 0; i < len; i++){
25362                 var n = cs[i];
25363                 n.previousSibling = cs[i-1];
25364                 n.nextSibling = cs[i+1];
25365                 if(i == 0){
25366                     this.setFirstChild(n);
25367                 }
25368                 if(i == len-1){
25369                     this.setLastChild(n);
25370                 }
25371             }
25372         }
25373     },
25374
25375     /**
25376      * Returns true if this node is an ancestor (at any point) of the passed node.
25377      * @param {Node} node
25378      * @return {Boolean}
25379      */
25380     contains : function(node){
25381         return node.isAncestor(this);
25382     },
25383
25384     /**
25385      * Returns true if the passed node is an ancestor (at any point) of this node.
25386      * @param {Node} node
25387      * @return {Boolean}
25388      */
25389     isAncestor : function(node){
25390         var p = this.parentNode;
25391         while(p){
25392             if(p == node){
25393                 return true;
25394             }
25395             p = p.parentNode;
25396         }
25397         return false;
25398     },
25399
25400     toString : function(){
25401         return "[Node"+(this.id?" "+this.id:"")+"]";
25402     }
25403 });/*
25404  * Based on:
25405  * Ext JS Library 1.1.1
25406  * Copyright(c) 2006-2007, Ext JS, LLC.
25407  *
25408  * Originally Released Under LGPL - original licence link has changed is not relivant.
25409  *
25410  * Fork - LGPL
25411  * <script type="text/javascript">
25412  */
25413  (function(){ 
25414 /**
25415  * @class Roo.Layer
25416  * @extends Roo.Element
25417  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
25418  * automatic maintaining of shadow/shim positions.
25419  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
25420  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
25421  * you can pass a string with a CSS class name. False turns off the shadow.
25422  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
25423  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
25424  * @cfg {String} cls CSS class to add to the element
25425  * @cfg {Number} zindex Starting z-index (defaults to 11000)
25426  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
25427  * @constructor
25428  * @param {Object} config An object with config options.
25429  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
25430  */
25431
25432 Roo.Layer = function(config, existingEl){
25433     config = config || {};
25434     var dh = Roo.DomHelper;
25435     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
25436     if(existingEl){
25437         this.dom = Roo.getDom(existingEl);
25438     }
25439     if(!this.dom){
25440         var o = config.dh || {tag: "div", cls: "x-layer"};
25441         this.dom = dh.append(pel, o);
25442     }
25443     if(config.cls){
25444         this.addClass(config.cls);
25445     }
25446     this.constrain = config.constrain !== false;
25447     this.visibilityMode = Roo.Element.VISIBILITY;
25448     if(config.id){
25449         this.id = this.dom.id = config.id;
25450     }else{
25451         this.id = Roo.id(this.dom);
25452     }
25453     this.zindex = config.zindex || this.getZIndex();
25454     this.position("absolute", this.zindex);
25455     if(config.shadow){
25456         this.shadowOffset = config.shadowOffset || 4;
25457         this.shadow = new Roo.Shadow({
25458             offset : this.shadowOffset,
25459             mode : config.shadow
25460         });
25461     }else{
25462         this.shadowOffset = 0;
25463     }
25464     this.useShim = config.shim !== false && Roo.useShims;
25465     this.useDisplay = config.useDisplay;
25466     this.hide();
25467 };
25468
25469 var supr = Roo.Element.prototype;
25470
25471 // shims are shared among layer to keep from having 100 iframes
25472 var shims = [];
25473
25474 Roo.extend(Roo.Layer, Roo.Element, {
25475
25476     getZIndex : function(){
25477         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
25478     },
25479
25480     getShim : function(){
25481         if(!this.useShim){
25482             return null;
25483         }
25484         if(this.shim){
25485             return this.shim;
25486         }
25487         var shim = shims.shift();
25488         if(!shim){
25489             shim = this.createShim();
25490             shim.enableDisplayMode('block');
25491             shim.dom.style.display = 'none';
25492             shim.dom.style.visibility = 'visible';
25493         }
25494         var pn = this.dom.parentNode;
25495         if(shim.dom.parentNode != pn){
25496             pn.insertBefore(shim.dom, this.dom);
25497         }
25498         shim.setStyle('z-index', this.getZIndex()-2);
25499         this.shim = shim;
25500         return shim;
25501     },
25502
25503     hideShim : function(){
25504         if(this.shim){
25505             this.shim.setDisplayed(false);
25506             shims.push(this.shim);
25507             delete this.shim;
25508         }
25509     },
25510
25511     disableShadow : function(){
25512         if(this.shadow){
25513             this.shadowDisabled = true;
25514             this.shadow.hide();
25515             this.lastShadowOffset = this.shadowOffset;
25516             this.shadowOffset = 0;
25517         }
25518     },
25519
25520     enableShadow : function(show){
25521         if(this.shadow){
25522             this.shadowDisabled = false;
25523             this.shadowOffset = this.lastShadowOffset;
25524             delete this.lastShadowOffset;
25525             if(show){
25526                 this.sync(true);
25527             }
25528         }
25529     },
25530
25531     // private
25532     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
25533     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
25534     sync : function(doShow){
25535         var sw = this.shadow;
25536         if(!this.updating && this.isVisible() && (sw || this.useShim)){
25537             var sh = this.getShim();
25538
25539             var w = this.getWidth(),
25540                 h = this.getHeight();
25541
25542             var l = this.getLeft(true),
25543                 t = this.getTop(true);
25544
25545             if(sw && !this.shadowDisabled){
25546                 if(doShow && !sw.isVisible()){
25547                     sw.show(this);
25548                 }else{
25549                     sw.realign(l, t, w, h);
25550                 }
25551                 if(sh){
25552                     if(doShow){
25553                        sh.show();
25554                     }
25555                     // fit the shim behind the shadow, so it is shimmed too
25556                     var a = sw.adjusts, s = sh.dom.style;
25557                     s.left = (Math.min(l, l+a.l))+"px";
25558                     s.top = (Math.min(t, t+a.t))+"px";
25559                     s.width = (w+a.w)+"px";
25560                     s.height = (h+a.h)+"px";
25561                 }
25562             }else if(sh){
25563                 if(doShow){
25564                    sh.show();
25565                 }
25566                 sh.setSize(w, h);
25567                 sh.setLeftTop(l, t);
25568             }
25569             
25570         }
25571     },
25572
25573     // private
25574     destroy : function(){
25575         this.hideShim();
25576         if(this.shadow){
25577             this.shadow.hide();
25578         }
25579         this.removeAllListeners();
25580         var pn = this.dom.parentNode;
25581         if(pn){
25582             pn.removeChild(this.dom);
25583         }
25584         Roo.Element.uncache(this.id);
25585     },
25586
25587     remove : function(){
25588         this.destroy();
25589     },
25590
25591     // private
25592     beginUpdate : function(){
25593         this.updating = true;
25594     },
25595
25596     // private
25597     endUpdate : function(){
25598         this.updating = false;
25599         this.sync(true);
25600     },
25601
25602     // private
25603     hideUnders : function(negOffset){
25604         if(this.shadow){
25605             this.shadow.hide();
25606         }
25607         this.hideShim();
25608     },
25609
25610     // private
25611     constrainXY : function(){
25612         if(this.constrain){
25613             var vw = Roo.lib.Dom.getViewWidth(),
25614                 vh = Roo.lib.Dom.getViewHeight();
25615             var s = Roo.get(document).getScroll();
25616
25617             var xy = this.getXY();
25618             var x = xy[0], y = xy[1];   
25619             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
25620             // only move it if it needs it
25621             var moved = false;
25622             // first validate right/bottom
25623             if((x + w) > vw+s.left){
25624                 x = vw - w - this.shadowOffset;
25625                 moved = true;
25626             }
25627             if((y + h) > vh+s.top){
25628                 y = vh - h - this.shadowOffset;
25629                 moved = true;
25630             }
25631             // then make sure top/left isn't negative
25632             if(x < s.left){
25633                 x = s.left;
25634                 moved = true;
25635             }
25636             if(y < s.top){
25637                 y = s.top;
25638                 moved = true;
25639             }
25640             if(moved){
25641                 if(this.avoidY){
25642                     var ay = this.avoidY;
25643                     if(y <= ay && (y+h) >= ay){
25644                         y = ay-h-5;   
25645                     }
25646                 }
25647                 xy = [x, y];
25648                 this.storeXY(xy);
25649                 supr.setXY.call(this, xy);
25650                 this.sync();
25651             }
25652         }
25653     },
25654
25655     isVisible : function(){
25656         return this.visible;    
25657     },
25658
25659     // private
25660     showAction : function(){
25661         this.visible = true; // track visibility to prevent getStyle calls
25662         if(this.useDisplay === true){
25663             this.setDisplayed("");
25664         }else if(this.lastXY){
25665             supr.setXY.call(this, this.lastXY);
25666         }else if(this.lastLT){
25667             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
25668         }
25669     },
25670
25671     // private
25672     hideAction : function(){
25673         this.visible = false;
25674         if(this.useDisplay === true){
25675             this.setDisplayed(false);
25676         }else{
25677             this.setLeftTop(-10000,-10000);
25678         }
25679     },
25680
25681     // overridden Element method
25682     setVisible : function(v, a, d, c, e){
25683         if(v){
25684             this.showAction();
25685         }
25686         if(a && v){
25687             var cb = function(){
25688                 this.sync(true);
25689                 if(c){
25690                     c();
25691                 }
25692             }.createDelegate(this);
25693             supr.setVisible.call(this, true, true, d, cb, e);
25694         }else{
25695             if(!v){
25696                 this.hideUnders(true);
25697             }
25698             var cb = c;
25699             if(a){
25700                 cb = function(){
25701                     this.hideAction();
25702                     if(c){
25703                         c();
25704                     }
25705                 }.createDelegate(this);
25706             }
25707             supr.setVisible.call(this, v, a, d, cb, e);
25708             if(v){
25709                 this.sync(true);
25710             }else if(!a){
25711                 this.hideAction();
25712             }
25713         }
25714     },
25715
25716     storeXY : function(xy){
25717         delete this.lastLT;
25718         this.lastXY = xy;
25719     },
25720
25721     storeLeftTop : function(left, top){
25722         delete this.lastXY;
25723         this.lastLT = [left, top];
25724     },
25725
25726     // private
25727     beforeFx : function(){
25728         this.beforeAction();
25729         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
25730     },
25731
25732     // private
25733     afterFx : function(){
25734         Roo.Layer.superclass.afterFx.apply(this, arguments);
25735         this.sync(this.isVisible());
25736     },
25737
25738     // private
25739     beforeAction : function(){
25740         if(!this.updating && this.shadow){
25741             this.shadow.hide();
25742         }
25743     },
25744
25745     // overridden Element method
25746     setLeft : function(left){
25747         this.storeLeftTop(left, this.getTop(true));
25748         supr.setLeft.apply(this, arguments);
25749         this.sync();
25750     },
25751
25752     setTop : function(top){
25753         this.storeLeftTop(this.getLeft(true), top);
25754         supr.setTop.apply(this, arguments);
25755         this.sync();
25756     },
25757
25758     setLeftTop : function(left, top){
25759         this.storeLeftTop(left, top);
25760         supr.setLeftTop.apply(this, arguments);
25761         this.sync();
25762     },
25763
25764     setXY : function(xy, a, d, c, e){
25765         this.fixDisplay();
25766         this.beforeAction();
25767         this.storeXY(xy);
25768         var cb = this.createCB(c);
25769         supr.setXY.call(this, xy, a, d, cb, e);
25770         if(!a){
25771             cb();
25772         }
25773     },
25774
25775     // private
25776     createCB : function(c){
25777         var el = this;
25778         return function(){
25779             el.constrainXY();
25780             el.sync(true);
25781             if(c){
25782                 c();
25783             }
25784         };
25785     },
25786
25787     // overridden Element method
25788     setX : function(x, a, d, c, e){
25789         this.setXY([x, this.getY()], a, d, c, e);
25790     },
25791
25792     // overridden Element method
25793     setY : function(y, a, d, c, e){
25794         this.setXY([this.getX(), y], a, d, c, e);
25795     },
25796
25797     // overridden Element method
25798     setSize : function(w, h, a, d, c, e){
25799         this.beforeAction();
25800         var cb = this.createCB(c);
25801         supr.setSize.call(this, w, h, a, d, cb, e);
25802         if(!a){
25803             cb();
25804         }
25805     },
25806
25807     // overridden Element method
25808     setWidth : function(w, a, d, c, e){
25809         this.beforeAction();
25810         var cb = this.createCB(c);
25811         supr.setWidth.call(this, w, a, d, cb, e);
25812         if(!a){
25813             cb();
25814         }
25815     },
25816
25817     // overridden Element method
25818     setHeight : function(h, a, d, c, e){
25819         this.beforeAction();
25820         var cb = this.createCB(c);
25821         supr.setHeight.call(this, h, a, d, cb, e);
25822         if(!a){
25823             cb();
25824         }
25825     },
25826
25827     // overridden Element method
25828     setBounds : function(x, y, w, h, a, d, c, e){
25829         this.beforeAction();
25830         var cb = this.createCB(c);
25831         if(!a){
25832             this.storeXY([x, y]);
25833             supr.setXY.call(this, [x, y]);
25834             supr.setSize.call(this, w, h, a, d, cb, e);
25835             cb();
25836         }else{
25837             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
25838         }
25839         return this;
25840     },
25841     
25842     /**
25843      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
25844      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
25845      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
25846      * @param {Number} zindex The new z-index to set
25847      * @return {this} The Layer
25848      */
25849     setZIndex : function(zindex){
25850         this.zindex = zindex;
25851         this.setStyle("z-index", zindex + 2);
25852         if(this.shadow){
25853             this.shadow.setZIndex(zindex + 1);
25854         }
25855         if(this.shim){
25856             this.shim.setStyle("z-index", zindex);
25857         }
25858     }
25859 });
25860 })();/*
25861  * Based on:
25862  * Ext JS Library 1.1.1
25863  * Copyright(c) 2006-2007, Ext JS, LLC.
25864  *
25865  * Originally Released Under LGPL - original licence link has changed is not relivant.
25866  *
25867  * Fork - LGPL
25868  * <script type="text/javascript">
25869  */
25870
25871
25872 /**
25873  * @class Roo.Shadow
25874  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
25875  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
25876  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
25877  * @constructor
25878  * Create a new Shadow
25879  * @param {Object} config The config object
25880  */
25881 Roo.Shadow = function(config){
25882     Roo.apply(this, config);
25883     if(typeof this.mode != "string"){
25884         this.mode = this.defaultMode;
25885     }
25886     var o = this.offset, a = {h: 0};
25887     var rad = Math.floor(this.offset/2);
25888     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
25889         case "drop":
25890             a.w = 0;
25891             a.l = a.t = o;
25892             a.t -= 1;
25893             if(Roo.isIE){
25894                 a.l -= this.offset + rad;
25895                 a.t -= this.offset + rad;
25896                 a.w -= rad;
25897                 a.h -= rad;
25898                 a.t += 1;
25899             }
25900         break;
25901         case "sides":
25902             a.w = (o*2);
25903             a.l = -o;
25904             a.t = o-1;
25905             if(Roo.isIE){
25906                 a.l -= (this.offset - rad);
25907                 a.t -= this.offset + rad;
25908                 a.l += 1;
25909                 a.w -= (this.offset - rad)*2;
25910                 a.w -= rad + 1;
25911                 a.h -= 1;
25912             }
25913         break;
25914         case "frame":
25915             a.w = a.h = (o*2);
25916             a.l = a.t = -o;
25917             a.t += 1;
25918             a.h -= 2;
25919             if(Roo.isIE){
25920                 a.l -= (this.offset - rad);
25921                 a.t -= (this.offset - rad);
25922                 a.l += 1;
25923                 a.w -= (this.offset + rad + 1);
25924                 a.h -= (this.offset + rad);
25925                 a.h += 1;
25926             }
25927         break;
25928     };
25929
25930     this.adjusts = a;
25931 };
25932
25933 Roo.Shadow.prototype = {
25934     /**
25935      * @cfg {String} mode
25936      * The shadow display mode.  Supports the following options:<br />
25937      * sides: Shadow displays on both sides and bottom only<br />
25938      * frame: Shadow displays equally on all four sides<br />
25939      * drop: Traditional bottom-right drop shadow (default)
25940      */
25941     /**
25942      * @cfg {String} offset
25943      * The number of pixels to offset the shadow from the element (defaults to 4)
25944      */
25945     offset: 4,
25946
25947     // private
25948     defaultMode: "drop",
25949
25950     /**
25951      * Displays the shadow under the target element
25952      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
25953      */
25954     show : function(target){
25955         target = Roo.get(target);
25956         if(!this.el){
25957             this.el = Roo.Shadow.Pool.pull();
25958             if(this.el.dom.nextSibling != target.dom){
25959                 this.el.insertBefore(target);
25960             }
25961         }
25962         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
25963         if(Roo.isIE){
25964             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
25965         }
25966         this.realign(
25967             target.getLeft(true),
25968             target.getTop(true),
25969             target.getWidth(),
25970             target.getHeight()
25971         );
25972         this.el.dom.style.display = "block";
25973     },
25974
25975     /**
25976      * Returns true if the shadow is visible, else false
25977      */
25978     isVisible : function(){
25979         return this.el ? true : false;  
25980     },
25981
25982     /**
25983      * Direct alignment when values are already available. Show must be called at least once before
25984      * calling this method to ensure it is initialized.
25985      * @param {Number} left The target element left position
25986      * @param {Number} top The target element top position
25987      * @param {Number} width The target element width
25988      * @param {Number} height The target element height
25989      */
25990     realign : function(l, t, w, h){
25991         if(!this.el){
25992             return;
25993         }
25994         var a = this.adjusts, d = this.el.dom, s = d.style;
25995         var iea = 0;
25996         s.left = (l+a.l)+"px";
25997         s.top = (t+a.t)+"px";
25998         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
25999  
26000         if(s.width != sws || s.height != shs){
26001             s.width = sws;
26002             s.height = shs;
26003             if(!Roo.isIE){
26004                 var cn = d.childNodes;
26005                 var sww = Math.max(0, (sw-12))+"px";
26006                 cn[0].childNodes[1].style.width = sww;
26007                 cn[1].childNodes[1].style.width = sww;
26008                 cn[2].childNodes[1].style.width = sww;
26009                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26010             }
26011         }
26012     },
26013
26014     /**
26015      * Hides this shadow
26016      */
26017     hide : function(){
26018         if(this.el){
26019             this.el.dom.style.display = "none";
26020             Roo.Shadow.Pool.push(this.el);
26021             delete this.el;
26022         }
26023     },
26024
26025     /**
26026      * Adjust the z-index of this shadow
26027      * @param {Number} zindex The new z-index
26028      */
26029     setZIndex : function(z){
26030         this.zIndex = z;
26031         if(this.el){
26032             this.el.setStyle("z-index", z);
26033         }
26034     }
26035 };
26036
26037 // Private utility class that manages the internal Shadow cache
26038 Roo.Shadow.Pool = function(){
26039     var p = [];
26040     var markup = Roo.isIE ?
26041                  '<div class="x-ie-shadow"></div>' :
26042                  '<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>';
26043     return {
26044         pull : function(){
26045             var sh = p.shift();
26046             if(!sh){
26047                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26048                 sh.autoBoxAdjust = false;
26049             }
26050             return sh;
26051         },
26052
26053         push : function(sh){
26054             p.push(sh);
26055         }
26056     };
26057 }();/*
26058  * Based on:
26059  * Ext JS Library 1.1.1
26060  * Copyright(c) 2006-2007, Ext JS, LLC.
26061  *
26062  * Originally Released Under LGPL - original licence link has changed is not relivant.
26063  *
26064  * Fork - LGPL
26065  * <script type="text/javascript">
26066  */
26067
26068
26069 /**
26070  * @class Roo.SplitBar
26071  * @extends Roo.util.Observable
26072  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26073  * <br><br>
26074  * Usage:
26075  * <pre><code>
26076 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26077                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26078 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26079 split.minSize = 100;
26080 split.maxSize = 600;
26081 split.animate = true;
26082 split.on('moved', splitterMoved);
26083 </code></pre>
26084  * @constructor
26085  * Create a new SplitBar
26086  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26087  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26088  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26089  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26090                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26091                         position of the SplitBar).
26092  */
26093 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26094     
26095     /** @private */
26096     this.el = Roo.get(dragElement, true);
26097     this.el.dom.unselectable = "on";
26098     /** @private */
26099     this.resizingEl = Roo.get(resizingElement, true);
26100
26101     /**
26102      * @private
26103      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26104      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26105      * @type Number
26106      */
26107     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26108     
26109     /**
26110      * The minimum size of the resizing element. (Defaults to 0)
26111      * @type Number
26112      */
26113     this.minSize = 0;
26114     
26115     /**
26116      * The maximum size of the resizing element. (Defaults to 2000)
26117      * @type Number
26118      */
26119     this.maxSize = 2000;
26120     
26121     /**
26122      * Whether to animate the transition to the new size
26123      * @type Boolean
26124      */
26125     this.animate = false;
26126     
26127     /**
26128      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26129      * @type Boolean
26130      */
26131     this.useShim = false;
26132     
26133     /** @private */
26134     this.shim = null;
26135     
26136     if(!existingProxy){
26137         /** @private */
26138         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26139     }else{
26140         this.proxy = Roo.get(existingProxy).dom;
26141     }
26142     /** @private */
26143     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26144     
26145     /** @private */
26146     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26147     
26148     /** @private */
26149     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26150     
26151     /** @private */
26152     this.dragSpecs = {};
26153     
26154     /**
26155      * @private The adapter to use to positon and resize elements
26156      */
26157     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26158     this.adapter.init(this);
26159     
26160     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26161         /** @private */
26162         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26163         this.el.addClass("x-splitbar-h");
26164     }else{
26165         /** @private */
26166         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26167         this.el.addClass("x-splitbar-v");
26168     }
26169     
26170     this.addEvents({
26171         /**
26172          * @event resize
26173          * Fires when the splitter is moved (alias for {@link #event-moved})
26174          * @param {Roo.SplitBar} this
26175          * @param {Number} newSize the new width or height
26176          */
26177         "resize" : true,
26178         /**
26179          * @event moved
26180          * Fires when the splitter is moved
26181          * @param {Roo.SplitBar} this
26182          * @param {Number} newSize the new width or height
26183          */
26184         "moved" : true,
26185         /**
26186          * @event beforeresize
26187          * Fires before the splitter is dragged
26188          * @param {Roo.SplitBar} this
26189          */
26190         "beforeresize" : true,
26191
26192         "beforeapply" : true
26193     });
26194
26195     Roo.util.Observable.call(this);
26196 };
26197
26198 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26199     onStartProxyDrag : function(x, y){
26200         this.fireEvent("beforeresize", this);
26201         if(!this.overlay){
26202             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26203             o.unselectable();
26204             o.enableDisplayMode("block");
26205             // all splitbars share the same overlay
26206             Roo.SplitBar.prototype.overlay = o;
26207         }
26208         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26209         this.overlay.show();
26210         Roo.get(this.proxy).setDisplayed("block");
26211         var size = this.adapter.getElementSize(this);
26212         this.activeMinSize = this.getMinimumSize();;
26213         this.activeMaxSize = this.getMaximumSize();;
26214         var c1 = size - this.activeMinSize;
26215         var c2 = Math.max(this.activeMaxSize - size, 0);
26216         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26217             this.dd.resetConstraints();
26218             this.dd.setXConstraint(
26219                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26220                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26221             );
26222             this.dd.setYConstraint(0, 0);
26223         }else{
26224             this.dd.resetConstraints();
26225             this.dd.setXConstraint(0, 0);
26226             this.dd.setYConstraint(
26227                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26228                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26229             );
26230          }
26231         this.dragSpecs.startSize = size;
26232         this.dragSpecs.startPoint = [x, y];
26233         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26234     },
26235     
26236     /** 
26237      * @private Called after the drag operation by the DDProxy
26238      */
26239     onEndProxyDrag : function(e){
26240         Roo.get(this.proxy).setDisplayed(false);
26241         var endPoint = Roo.lib.Event.getXY(e);
26242         if(this.overlay){
26243             this.overlay.hide();
26244         }
26245         var newSize;
26246         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26247             newSize = this.dragSpecs.startSize + 
26248                 (this.placement == Roo.SplitBar.LEFT ?
26249                     endPoint[0] - this.dragSpecs.startPoint[0] :
26250                     this.dragSpecs.startPoint[0] - endPoint[0]
26251                 );
26252         }else{
26253             newSize = this.dragSpecs.startSize + 
26254                 (this.placement == Roo.SplitBar.TOP ?
26255                     endPoint[1] - this.dragSpecs.startPoint[1] :
26256                     this.dragSpecs.startPoint[1] - endPoint[1]
26257                 );
26258         }
26259         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26260         if(newSize != this.dragSpecs.startSize){
26261             if(this.fireEvent('beforeapply', this, newSize) !== false){
26262                 this.adapter.setElementSize(this, newSize);
26263                 this.fireEvent("moved", this, newSize);
26264                 this.fireEvent("resize", this, newSize);
26265             }
26266         }
26267     },
26268     
26269     /**
26270      * Get the adapter this SplitBar uses
26271      * @return The adapter object
26272      */
26273     getAdapter : function(){
26274         return this.adapter;
26275     },
26276     
26277     /**
26278      * Set the adapter this SplitBar uses
26279      * @param {Object} adapter A SplitBar adapter object
26280      */
26281     setAdapter : function(adapter){
26282         this.adapter = adapter;
26283         this.adapter.init(this);
26284     },
26285     
26286     /**
26287      * Gets the minimum size for the resizing element
26288      * @return {Number} The minimum size
26289      */
26290     getMinimumSize : function(){
26291         return this.minSize;
26292     },
26293     
26294     /**
26295      * Sets the minimum size for the resizing element
26296      * @param {Number} minSize The minimum size
26297      */
26298     setMinimumSize : function(minSize){
26299         this.minSize = minSize;
26300     },
26301     
26302     /**
26303      * Gets the maximum size for the resizing element
26304      * @return {Number} The maximum size
26305      */
26306     getMaximumSize : function(){
26307         return this.maxSize;
26308     },
26309     
26310     /**
26311      * Sets the maximum size for the resizing element
26312      * @param {Number} maxSize The maximum size
26313      */
26314     setMaximumSize : function(maxSize){
26315         this.maxSize = maxSize;
26316     },
26317     
26318     /**
26319      * Sets the initialize size for the resizing element
26320      * @param {Number} size The initial size
26321      */
26322     setCurrentSize : function(size){
26323         var oldAnimate = this.animate;
26324         this.animate = false;
26325         this.adapter.setElementSize(this, size);
26326         this.animate = oldAnimate;
26327     },
26328     
26329     /**
26330      * Destroy this splitbar. 
26331      * @param {Boolean} removeEl True to remove the element
26332      */
26333     destroy : function(removeEl){
26334         if(this.shim){
26335             this.shim.remove();
26336         }
26337         this.dd.unreg();
26338         this.proxy.parentNode.removeChild(this.proxy);
26339         if(removeEl){
26340             this.el.remove();
26341         }
26342     }
26343 });
26344
26345 /**
26346  * @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.
26347  */
26348 Roo.SplitBar.createProxy = function(dir){
26349     var proxy = new Roo.Element(document.createElement("div"));
26350     proxy.unselectable();
26351     var cls = 'x-splitbar-proxy';
26352     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26353     document.body.appendChild(proxy.dom);
26354     return proxy.dom;
26355 };
26356
26357 /** 
26358  * @class Roo.SplitBar.BasicLayoutAdapter
26359  * Default Adapter. It assumes the splitter and resizing element are not positioned
26360  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26361  */
26362 Roo.SplitBar.BasicLayoutAdapter = function(){
26363 };
26364
26365 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26366     // do nothing for now
26367     init : function(s){
26368     
26369     },
26370     /**
26371      * Called before drag operations to get the current size of the resizing element. 
26372      * @param {Roo.SplitBar} s The SplitBar using this adapter
26373      */
26374      getElementSize : function(s){
26375         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26376             return s.resizingEl.getWidth();
26377         }else{
26378             return s.resizingEl.getHeight();
26379         }
26380     },
26381     
26382     /**
26383      * Called after drag operations to set the size of the resizing element.
26384      * @param {Roo.SplitBar} s The SplitBar using this adapter
26385      * @param {Number} newSize The new size to set
26386      * @param {Function} onComplete A function to be invoked when resizing is complete
26387      */
26388     setElementSize : function(s, newSize, onComplete){
26389         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26390             if(!s.animate){
26391                 s.resizingEl.setWidth(newSize);
26392                 if(onComplete){
26393                     onComplete(s, newSize);
26394                 }
26395             }else{
26396                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26397             }
26398         }else{
26399             
26400             if(!s.animate){
26401                 s.resizingEl.setHeight(newSize);
26402                 if(onComplete){
26403                     onComplete(s, newSize);
26404                 }
26405             }else{
26406                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26407             }
26408         }
26409     }
26410 };
26411
26412 /** 
26413  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26414  * @extends Roo.SplitBar.BasicLayoutAdapter
26415  * Adapter that  moves the splitter element to align with the resized sizing element. 
26416  * Used with an absolute positioned SplitBar.
26417  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26418  * document.body, make sure you assign an id to the body element.
26419  */
26420 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26421     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26422     this.container = Roo.get(container);
26423 };
26424
26425 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26426     init : function(s){
26427         this.basic.init(s);
26428     },
26429     
26430     getElementSize : function(s){
26431         return this.basic.getElementSize(s);
26432     },
26433     
26434     setElementSize : function(s, newSize, onComplete){
26435         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26436     },
26437     
26438     moveSplitter : function(s){
26439         var yes = Roo.SplitBar;
26440         switch(s.placement){
26441             case yes.LEFT:
26442                 s.el.setX(s.resizingEl.getRight());
26443                 break;
26444             case yes.RIGHT:
26445                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26446                 break;
26447             case yes.TOP:
26448                 s.el.setY(s.resizingEl.getBottom());
26449                 break;
26450             case yes.BOTTOM:
26451                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26452                 break;
26453         }
26454     }
26455 };
26456
26457 /**
26458  * Orientation constant - Create a vertical SplitBar
26459  * @static
26460  * @type Number
26461  */
26462 Roo.SplitBar.VERTICAL = 1;
26463
26464 /**
26465  * Orientation constant - Create a horizontal SplitBar
26466  * @static
26467  * @type Number
26468  */
26469 Roo.SplitBar.HORIZONTAL = 2;
26470
26471 /**
26472  * Placement constant - The resizing element is to the left of the splitter element
26473  * @static
26474  * @type Number
26475  */
26476 Roo.SplitBar.LEFT = 1;
26477
26478 /**
26479  * Placement constant - The resizing element is to the right of the splitter element
26480  * @static
26481  * @type Number
26482  */
26483 Roo.SplitBar.RIGHT = 2;
26484
26485 /**
26486  * Placement constant - The resizing element is positioned above the splitter element
26487  * @static
26488  * @type Number
26489  */
26490 Roo.SplitBar.TOP = 3;
26491
26492 /**
26493  * Placement constant - The resizing element is positioned under splitter element
26494  * @static
26495  * @type Number
26496  */
26497 Roo.SplitBar.BOTTOM = 4;
26498 /*
26499  * Based on:
26500  * Ext JS Library 1.1.1
26501  * Copyright(c) 2006-2007, Ext JS, LLC.
26502  *
26503  * Originally Released Under LGPL - original licence link has changed is not relivant.
26504  *
26505  * Fork - LGPL
26506  * <script type="text/javascript">
26507  */
26508
26509 /**
26510  * @class Roo.View
26511  * @extends Roo.util.Observable
26512  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26513  * This class also supports single and multi selection modes. <br>
26514  * Create a data model bound view:
26515  <pre><code>
26516  var store = new Roo.data.Store(...);
26517
26518  var view = new Roo.View({
26519     el : "my-element",
26520     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26521  
26522     singleSelect: true,
26523     selectedClass: "ydataview-selected",
26524     store: store
26525  });
26526
26527  // listen for node click?
26528  view.on("click", function(vw, index, node, e){
26529  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26530  });
26531
26532  // load XML data
26533  dataModel.load("foobar.xml");
26534  </code></pre>
26535  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26536  * <br><br>
26537  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26538  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26539  * 
26540  * Note: old style constructor is still suported (container, template, config)
26541  * 
26542  * @constructor
26543  * Create a new View
26544  * @param {Object} config The config object
26545  * 
26546  */
26547 Roo.View = function(config, depreciated_tpl, depreciated_config){
26548     
26549     this.parent = false;
26550     
26551     if (typeof(depreciated_tpl) == 'undefined') {
26552         // new way.. - universal constructor.
26553         Roo.apply(this, config);
26554         this.el  = Roo.get(this.el);
26555     } else {
26556         // old format..
26557         this.el  = Roo.get(config);
26558         this.tpl = depreciated_tpl;
26559         Roo.apply(this, depreciated_config);
26560     }
26561     this.wrapEl  = this.el.wrap().wrap();
26562     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26563     
26564     
26565     if(typeof(this.tpl) == "string"){
26566         this.tpl = new Roo.Template(this.tpl);
26567     } else {
26568         // support xtype ctors..
26569         this.tpl = new Roo.factory(this.tpl, Roo);
26570     }
26571     
26572     
26573     this.tpl.compile();
26574     
26575     /** @private */
26576     this.addEvents({
26577         /**
26578          * @event beforeclick
26579          * Fires before a click is processed. Returns false to cancel the default action.
26580          * @param {Roo.View} this
26581          * @param {Number} index The index of the target node
26582          * @param {HTMLElement} node The target node
26583          * @param {Roo.EventObject} e The raw event object
26584          */
26585             "beforeclick" : true,
26586         /**
26587          * @event click
26588          * Fires when a template node is clicked.
26589          * @param {Roo.View} this
26590          * @param {Number} index The index of the target node
26591          * @param {HTMLElement} node The target node
26592          * @param {Roo.EventObject} e The raw event object
26593          */
26594             "click" : true,
26595         /**
26596          * @event dblclick
26597          * Fires when a template node is double clicked.
26598          * @param {Roo.View} this
26599          * @param {Number} index The index of the target node
26600          * @param {HTMLElement} node The target node
26601          * @param {Roo.EventObject} e The raw event object
26602          */
26603             "dblclick" : true,
26604         /**
26605          * @event contextmenu
26606          * Fires when a template node is right clicked.
26607          * @param {Roo.View} this
26608          * @param {Number} index The index of the target node
26609          * @param {HTMLElement} node The target node
26610          * @param {Roo.EventObject} e The raw event object
26611          */
26612             "contextmenu" : true,
26613         /**
26614          * @event selectionchange
26615          * Fires when the selected nodes change.
26616          * @param {Roo.View} this
26617          * @param {Array} selections Array of the selected nodes
26618          */
26619             "selectionchange" : true,
26620     
26621         /**
26622          * @event beforeselect
26623          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26624          * @param {Roo.View} this
26625          * @param {HTMLElement} node The node to be selected
26626          * @param {Array} selections Array of currently selected nodes
26627          */
26628             "beforeselect" : true,
26629         /**
26630          * @event preparedata
26631          * Fires on every row to render, to allow you to change the data.
26632          * @param {Roo.View} this
26633          * @param {Object} data to be rendered (change this)
26634          */
26635           "preparedata" : true
26636           
26637           
26638         });
26639
26640
26641
26642     this.el.on({
26643         "click": this.onClick,
26644         "dblclick": this.onDblClick,
26645         "contextmenu": this.onContextMenu,
26646         scope:this
26647     });
26648
26649     this.selections = [];
26650     this.nodes = [];
26651     this.cmp = new Roo.CompositeElementLite([]);
26652     if(this.store){
26653         this.store = Roo.factory(this.store, Roo.data);
26654         this.setStore(this.store, true);
26655     }
26656     
26657     if ( this.footer && this.footer.xtype) {
26658            
26659          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26660         
26661         this.footer.dataSource = this.store;
26662         this.footer.container = fctr;
26663         this.footer = Roo.factory(this.footer, Roo);
26664         fctr.insertFirst(this.el);
26665         
26666         // this is a bit insane - as the paging toolbar seems to detach the el..
26667 //        dom.parentNode.parentNode.parentNode
26668          // they get detached?
26669     }
26670     
26671     
26672     Roo.View.superclass.constructor.call(this);
26673     
26674     
26675 };
26676
26677 Roo.extend(Roo.View, Roo.util.Observable, {
26678     
26679      /**
26680      * @cfg {Roo.data.Store} store Data store to load data from.
26681      */
26682     store : false,
26683     
26684     /**
26685      * @cfg {String|Roo.Element} el The container element.
26686      */
26687     el : '',
26688     
26689     /**
26690      * @cfg {String|Roo.Template} tpl The template used by this View 
26691      */
26692     tpl : false,
26693     /**
26694      * @cfg {String} dataName the named area of the template to use as the data area
26695      *                          Works with domtemplates roo-name="name"
26696      */
26697     dataName: false,
26698     /**
26699      * @cfg {String} selectedClass The css class to add to selected nodes
26700      */
26701     selectedClass : "x-view-selected",
26702      /**
26703      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26704      */
26705     emptyText : "",
26706     
26707     /**
26708      * @cfg {String} text to display on mask (default Loading)
26709      */
26710     mask : false,
26711     /**
26712      * @cfg {Boolean} multiSelect Allow multiple selection
26713      */
26714     multiSelect : false,
26715     /**
26716      * @cfg {Boolean} singleSelect Allow single selection
26717      */
26718     singleSelect:  false,
26719     
26720     /**
26721      * @cfg {Boolean} toggleSelect - selecting 
26722      */
26723     toggleSelect : false,
26724     
26725     /**
26726      * @cfg {Boolean} tickable - selecting 
26727      */
26728     tickable : false,
26729     
26730     /**
26731      * Returns the element this view is bound to.
26732      * @return {Roo.Element}
26733      */
26734     getEl : function(){
26735         return this.wrapEl;
26736     },
26737     
26738     
26739
26740     /**
26741      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26742      */
26743     refresh : function(){
26744         //Roo.log('refresh');
26745         var t = this.tpl;
26746         
26747         // if we are using something like 'domtemplate', then
26748         // the what gets used is:
26749         // t.applySubtemplate(NAME, data, wrapping data..)
26750         // the outer template then get' applied with
26751         //     the store 'extra data'
26752         // and the body get's added to the
26753         //      roo-name="data" node?
26754         //      <span class='roo-tpl-{name}'></span> ?????
26755         
26756         
26757         
26758         this.clearSelections();
26759         this.el.update("");
26760         var html = [];
26761         var records = this.store.getRange();
26762         if(records.length < 1) {
26763             
26764             // is this valid??  = should it render a template??
26765             
26766             this.el.update(this.emptyText);
26767             return;
26768         }
26769         var el = this.el;
26770         if (this.dataName) {
26771             this.el.update(t.apply(this.store.meta)); //????
26772             el = this.el.child('.roo-tpl-' + this.dataName);
26773         }
26774         
26775         for(var i = 0, len = records.length; i < len; i++){
26776             var data = this.prepareData(records[i].data, i, records[i]);
26777             this.fireEvent("preparedata", this, data, i, records[i]);
26778             
26779             var d = Roo.apply({}, data);
26780             
26781             if(this.tickable){
26782                 Roo.apply(d, {'roo-id' : Roo.id()});
26783                 
26784                 var _this = this;
26785             
26786                 Roo.each(this.parent.item, function(item){
26787                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
26788                         return;
26789                     }
26790                     Roo.apply(d, {'roo-data-checked' : 'checked'});
26791                 });
26792             }
26793             
26794             html[html.length] = Roo.util.Format.trim(
26795                 this.dataName ?
26796                     t.applySubtemplate(this.dataName, d, this.store.meta) :
26797                     t.apply(d)
26798             );
26799         }
26800         
26801         
26802         
26803         el.update(html.join(""));
26804         this.nodes = el.dom.childNodes;
26805         this.updateIndexes(0);
26806     },
26807     
26808
26809     /**
26810      * Function to override to reformat the data that is sent to
26811      * the template for each node.
26812      * DEPRICATED - use the preparedata event handler.
26813      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
26814      * a JSON object for an UpdateManager bound view).
26815      */
26816     prepareData : function(data, index, record)
26817     {
26818         this.fireEvent("preparedata", this, data, index, record);
26819         return data;
26820     },
26821
26822     onUpdate : function(ds, record){
26823         // Roo.log('on update');   
26824         this.clearSelections();
26825         var index = this.store.indexOf(record);
26826         var n = this.nodes[index];
26827         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
26828         n.parentNode.removeChild(n);
26829         this.updateIndexes(index, index);
26830     },
26831
26832     
26833     
26834 // --------- FIXME     
26835     onAdd : function(ds, records, index)
26836     {
26837         //Roo.log(['on Add', ds, records, index] );        
26838         this.clearSelections();
26839         if(this.nodes.length == 0){
26840             this.refresh();
26841             return;
26842         }
26843         var n = this.nodes[index];
26844         for(var i = 0, len = records.length; i < len; i++){
26845             var d = this.prepareData(records[i].data, i, records[i]);
26846             if(n){
26847                 this.tpl.insertBefore(n, d);
26848             }else{
26849                 
26850                 this.tpl.append(this.el, d);
26851             }
26852         }
26853         this.updateIndexes(index);
26854     },
26855
26856     onRemove : function(ds, record, index){
26857        // Roo.log('onRemove');
26858         this.clearSelections();
26859         var el = this.dataName  ?
26860             this.el.child('.roo-tpl-' + this.dataName) :
26861             this.el; 
26862         
26863         el.dom.removeChild(this.nodes[index]);
26864         this.updateIndexes(index);
26865     },
26866
26867     /**
26868      * Refresh an individual node.
26869      * @param {Number} index
26870      */
26871     refreshNode : function(index){
26872         this.onUpdate(this.store, this.store.getAt(index));
26873     },
26874
26875     updateIndexes : function(startIndex, endIndex){
26876         var ns = this.nodes;
26877         startIndex = startIndex || 0;
26878         endIndex = endIndex || ns.length - 1;
26879         for(var i = startIndex; i <= endIndex; i++){
26880             ns[i].nodeIndex = i;
26881         }
26882     },
26883
26884     /**
26885      * Changes the data store this view uses and refresh the view.
26886      * @param {Store} store
26887      */
26888     setStore : function(store, initial){
26889         if(!initial && this.store){
26890             this.store.un("datachanged", this.refresh);
26891             this.store.un("add", this.onAdd);
26892             this.store.un("remove", this.onRemove);
26893             this.store.un("update", this.onUpdate);
26894             this.store.un("clear", this.refresh);
26895             this.store.un("beforeload", this.onBeforeLoad);
26896             this.store.un("load", this.onLoad);
26897             this.store.un("loadexception", this.onLoad);
26898         }
26899         if(store){
26900           
26901             store.on("datachanged", this.refresh, this);
26902             store.on("add", this.onAdd, this);
26903             store.on("remove", this.onRemove, this);
26904             store.on("update", this.onUpdate, this);
26905             store.on("clear", this.refresh, this);
26906             store.on("beforeload", this.onBeforeLoad, this);
26907             store.on("load", this.onLoad, this);
26908             store.on("loadexception", this.onLoad, this);
26909         }
26910         
26911         if(store){
26912             this.refresh();
26913         }
26914     },
26915     /**
26916      * onbeforeLoad - masks the loading area.
26917      *
26918      */
26919     onBeforeLoad : function(store,opts)
26920     {
26921          //Roo.log('onBeforeLoad');   
26922         if (!opts.add) {
26923             this.el.update("");
26924         }
26925         this.el.mask(this.mask ? this.mask : "Loading" ); 
26926     },
26927     onLoad : function ()
26928     {
26929         this.el.unmask();
26930     },
26931     
26932
26933     /**
26934      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
26935      * @param {HTMLElement} node
26936      * @return {HTMLElement} The template node
26937      */
26938     findItemFromChild : function(node){
26939         var el = this.dataName  ?
26940             this.el.child('.roo-tpl-' + this.dataName,true) :
26941             this.el.dom; 
26942         
26943         if(!node || node.parentNode == el){
26944                     return node;
26945             }
26946             var p = node.parentNode;
26947             while(p && p != el){
26948             if(p.parentNode == el){
26949                 return p;
26950             }
26951             p = p.parentNode;
26952         }
26953             return null;
26954     },
26955
26956     /** @ignore */
26957     onClick : function(e){
26958         var item = this.findItemFromChild(e.getTarget());
26959         if(item){
26960             var index = this.indexOf(item);
26961             if(this.onItemClick(item, index, e) !== false){
26962                 this.fireEvent("click", this, index, item, e);
26963             }
26964         }else{
26965             this.clearSelections();
26966         }
26967     },
26968
26969     /** @ignore */
26970     onContextMenu : function(e){
26971         var item = this.findItemFromChild(e.getTarget());
26972         if(item){
26973             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
26974         }
26975     },
26976
26977     /** @ignore */
26978     onDblClick : function(e){
26979         var item = this.findItemFromChild(e.getTarget());
26980         if(item){
26981             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
26982         }
26983     },
26984
26985     onItemClick : function(item, index, e)
26986     {
26987         if(this.fireEvent("beforeclick", this, index, item, e) === false){
26988             return false;
26989         }
26990         if (this.toggleSelect) {
26991             var m = this.isSelected(item) ? 'unselect' : 'select';
26992             //Roo.log(m);
26993             var _t = this;
26994             _t[m](item, true, false);
26995             return true;
26996         }
26997         if(this.multiSelect || this.singleSelect){
26998             if(this.multiSelect && e.shiftKey && this.lastSelection){
26999                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27000             }else{
27001                 this.select(item, this.multiSelect && e.ctrlKey);
27002                 this.lastSelection = item;
27003             }
27004             
27005             if(!this.tickable){
27006                 e.preventDefault();
27007             }
27008             
27009         }
27010         return true;
27011     },
27012
27013     /**
27014      * Get the number of selected nodes.
27015      * @return {Number}
27016      */
27017     getSelectionCount : function(){
27018         return this.selections.length;
27019     },
27020
27021     /**
27022      * Get the currently selected nodes.
27023      * @return {Array} An array of HTMLElements
27024      */
27025     getSelectedNodes : function(){
27026         return this.selections;
27027     },
27028
27029     /**
27030      * Get the indexes of the selected nodes.
27031      * @return {Array}
27032      */
27033     getSelectedIndexes : function(){
27034         var indexes = [], s = this.selections;
27035         for(var i = 0, len = s.length; i < len; i++){
27036             indexes.push(s[i].nodeIndex);
27037         }
27038         return indexes;
27039     },
27040
27041     /**
27042      * Clear all selections
27043      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27044      */
27045     clearSelections : function(suppressEvent){
27046         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27047             this.cmp.elements = this.selections;
27048             this.cmp.removeClass(this.selectedClass);
27049             this.selections = [];
27050             if(!suppressEvent){
27051                 this.fireEvent("selectionchange", this, this.selections);
27052             }
27053         }
27054     },
27055
27056     /**
27057      * Returns true if the passed node is selected
27058      * @param {HTMLElement/Number} node The node or node index
27059      * @return {Boolean}
27060      */
27061     isSelected : function(node){
27062         var s = this.selections;
27063         if(s.length < 1){
27064             return false;
27065         }
27066         node = this.getNode(node);
27067         return s.indexOf(node) !== -1;
27068     },
27069
27070     /**
27071      * Selects nodes.
27072      * @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
27073      * @param {Boolean} keepExisting (optional) true to keep existing selections
27074      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27075      */
27076     select : function(nodeInfo, keepExisting, suppressEvent){
27077         if(nodeInfo instanceof Array){
27078             if(!keepExisting){
27079                 this.clearSelections(true);
27080             }
27081             for(var i = 0, len = nodeInfo.length; i < len; i++){
27082                 this.select(nodeInfo[i], true, true);
27083             }
27084             return;
27085         } 
27086         var node = this.getNode(nodeInfo);
27087         if(!node || this.isSelected(node)){
27088             return; // already selected.
27089         }
27090         if(!keepExisting){
27091             this.clearSelections(true);
27092         }
27093         
27094         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27095             Roo.fly(node).addClass(this.selectedClass);
27096             this.selections.push(node);
27097             if(!suppressEvent){
27098                 this.fireEvent("selectionchange", this, this.selections);
27099             }
27100         }
27101         
27102         
27103     },
27104       /**
27105      * Unselects nodes.
27106      * @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
27107      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27108      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27109      */
27110     unselect : function(nodeInfo, keepExisting, suppressEvent)
27111     {
27112         if(nodeInfo instanceof Array){
27113             Roo.each(this.selections, function(s) {
27114                 this.unselect(s, nodeInfo);
27115             }, this);
27116             return;
27117         }
27118         var node = this.getNode(nodeInfo);
27119         if(!node || !this.isSelected(node)){
27120             //Roo.log("not selected");
27121             return; // not selected.
27122         }
27123         // fireevent???
27124         var ns = [];
27125         Roo.each(this.selections, function(s) {
27126             if (s == node ) {
27127                 Roo.fly(node).removeClass(this.selectedClass);
27128
27129                 return;
27130             }
27131             ns.push(s);
27132         },this);
27133         
27134         this.selections= ns;
27135         this.fireEvent("selectionchange", this, this.selections);
27136     },
27137
27138     /**
27139      * Gets a template node.
27140      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27141      * @return {HTMLElement} The node or null if it wasn't found
27142      */
27143     getNode : function(nodeInfo){
27144         if(typeof nodeInfo == "string"){
27145             return document.getElementById(nodeInfo);
27146         }else if(typeof nodeInfo == "number"){
27147             return this.nodes[nodeInfo];
27148         }
27149         return nodeInfo;
27150     },
27151
27152     /**
27153      * Gets a range template nodes.
27154      * @param {Number} startIndex
27155      * @param {Number} endIndex
27156      * @return {Array} An array of nodes
27157      */
27158     getNodes : function(start, end){
27159         var ns = this.nodes;
27160         start = start || 0;
27161         end = typeof end == "undefined" ? ns.length - 1 : end;
27162         var nodes = [];
27163         if(start <= end){
27164             for(var i = start; i <= end; i++){
27165                 nodes.push(ns[i]);
27166             }
27167         } else{
27168             for(var i = start; i >= end; i--){
27169                 nodes.push(ns[i]);
27170             }
27171         }
27172         return nodes;
27173     },
27174
27175     /**
27176      * Finds the index of the passed node
27177      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27178      * @return {Number} The index of the node or -1
27179      */
27180     indexOf : function(node){
27181         node = this.getNode(node);
27182         if(typeof node.nodeIndex == "number"){
27183             return node.nodeIndex;
27184         }
27185         var ns = this.nodes;
27186         for(var i = 0, len = ns.length; i < len; i++){
27187             if(ns[i] == node){
27188                 return i;
27189             }
27190         }
27191         return -1;
27192     }
27193 });
27194 /*
27195  * Based on:
27196  * Ext JS Library 1.1.1
27197  * Copyright(c) 2006-2007, Ext JS, LLC.
27198  *
27199  * Originally Released Under LGPL - original licence link has changed is not relivant.
27200  *
27201  * Fork - LGPL
27202  * <script type="text/javascript">
27203  */
27204
27205 /**
27206  * @class Roo.JsonView
27207  * @extends Roo.View
27208  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27209 <pre><code>
27210 var view = new Roo.JsonView({
27211     container: "my-element",
27212     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27213     multiSelect: true, 
27214     jsonRoot: "data" 
27215 });
27216
27217 // listen for node click?
27218 view.on("click", function(vw, index, node, e){
27219     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27220 });
27221
27222 // direct load of JSON data
27223 view.load("foobar.php");
27224
27225 // Example from my blog list
27226 var tpl = new Roo.Template(
27227     '&lt;div class="entry"&gt;' +
27228     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27229     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27230     "&lt;/div&gt;&lt;hr /&gt;"
27231 );
27232
27233 var moreView = new Roo.JsonView({
27234     container :  "entry-list", 
27235     template : tpl,
27236     jsonRoot: "posts"
27237 });
27238 moreView.on("beforerender", this.sortEntries, this);
27239 moreView.load({
27240     url: "/blog/get-posts.php",
27241     params: "allposts=true",
27242     text: "Loading Blog Entries..."
27243 });
27244 </code></pre>
27245
27246 * Note: old code is supported with arguments : (container, template, config)
27247
27248
27249  * @constructor
27250  * Create a new JsonView
27251  * 
27252  * @param {Object} config The config object
27253  * 
27254  */
27255 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27256     
27257     
27258     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27259
27260     var um = this.el.getUpdateManager();
27261     um.setRenderer(this);
27262     um.on("update", this.onLoad, this);
27263     um.on("failure", this.onLoadException, this);
27264
27265     /**
27266      * @event beforerender
27267      * Fires before rendering of the downloaded JSON data.
27268      * @param {Roo.JsonView} this
27269      * @param {Object} data The JSON data loaded
27270      */
27271     /**
27272      * @event load
27273      * Fires when data is loaded.
27274      * @param {Roo.JsonView} this
27275      * @param {Object} data The JSON data loaded
27276      * @param {Object} response The raw Connect response object
27277      */
27278     /**
27279      * @event loadexception
27280      * Fires when loading fails.
27281      * @param {Roo.JsonView} this
27282      * @param {Object} response The raw Connect response object
27283      */
27284     this.addEvents({
27285         'beforerender' : true,
27286         'load' : true,
27287         'loadexception' : true
27288     });
27289 };
27290 Roo.extend(Roo.JsonView, Roo.View, {
27291     /**
27292      * @type {String} The root property in the loaded JSON object that contains the data
27293      */
27294     jsonRoot : "",
27295
27296     /**
27297      * Refreshes the view.
27298      */
27299     refresh : function(){
27300         this.clearSelections();
27301         this.el.update("");
27302         var html = [];
27303         var o = this.jsonData;
27304         if(o && o.length > 0){
27305             for(var i = 0, len = o.length; i < len; i++){
27306                 var data = this.prepareData(o[i], i, o);
27307                 html[html.length] = this.tpl.apply(data);
27308             }
27309         }else{
27310             html.push(this.emptyText);
27311         }
27312         this.el.update(html.join(""));
27313         this.nodes = this.el.dom.childNodes;
27314         this.updateIndexes(0);
27315     },
27316
27317     /**
27318      * 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.
27319      * @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:
27320      <pre><code>
27321      view.load({
27322          url: "your-url.php",
27323          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27324          callback: yourFunction,
27325          scope: yourObject, //(optional scope)
27326          discardUrl: false,
27327          nocache: false,
27328          text: "Loading...",
27329          timeout: 30,
27330          scripts: false
27331      });
27332      </code></pre>
27333      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27334      * 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.
27335      * @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}
27336      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27337      * @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.
27338      */
27339     load : function(){
27340         var um = this.el.getUpdateManager();
27341         um.update.apply(um, arguments);
27342     },
27343
27344     // note - render is a standard framework call...
27345     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27346     render : function(el, response){
27347         
27348         this.clearSelections();
27349         this.el.update("");
27350         var o;
27351         try{
27352             if (response != '') {
27353                 o = Roo.util.JSON.decode(response.responseText);
27354                 if(this.jsonRoot){
27355                     
27356                     o = o[this.jsonRoot];
27357                 }
27358             }
27359         } catch(e){
27360         }
27361         /**
27362          * The current JSON data or null
27363          */
27364         this.jsonData = o;
27365         this.beforeRender();
27366         this.refresh();
27367     },
27368
27369 /**
27370  * Get the number of records in the current JSON dataset
27371  * @return {Number}
27372  */
27373     getCount : function(){
27374         return this.jsonData ? this.jsonData.length : 0;
27375     },
27376
27377 /**
27378  * Returns the JSON object for the specified node(s)
27379  * @param {HTMLElement/Array} node The node or an array of nodes
27380  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27381  * you get the JSON object for the node
27382  */
27383     getNodeData : function(node){
27384         if(node instanceof Array){
27385             var data = [];
27386             for(var i = 0, len = node.length; i < len; i++){
27387                 data.push(this.getNodeData(node[i]));
27388             }
27389             return data;
27390         }
27391         return this.jsonData[this.indexOf(node)] || null;
27392     },
27393
27394     beforeRender : function(){
27395         this.snapshot = this.jsonData;
27396         if(this.sortInfo){
27397             this.sort.apply(this, this.sortInfo);
27398         }
27399         this.fireEvent("beforerender", this, this.jsonData);
27400     },
27401
27402     onLoad : function(el, o){
27403         this.fireEvent("load", this, this.jsonData, o);
27404     },
27405
27406     onLoadException : function(el, o){
27407         this.fireEvent("loadexception", this, o);
27408     },
27409
27410 /**
27411  * Filter the data by a specific property.
27412  * @param {String} property A property on your JSON objects
27413  * @param {String/RegExp} value Either string that the property values
27414  * should start with, or a RegExp to test against the property
27415  */
27416     filter : function(property, value){
27417         if(this.jsonData){
27418             var data = [];
27419             var ss = this.snapshot;
27420             if(typeof value == "string"){
27421                 var vlen = value.length;
27422                 if(vlen == 0){
27423                     this.clearFilter();
27424                     return;
27425                 }
27426                 value = value.toLowerCase();
27427                 for(var i = 0, len = ss.length; i < len; i++){
27428                     var o = ss[i];
27429                     if(o[property].substr(0, vlen).toLowerCase() == value){
27430                         data.push(o);
27431                     }
27432                 }
27433             } else if(value.exec){ // regex?
27434                 for(var i = 0, len = ss.length; i < len; i++){
27435                     var o = ss[i];
27436                     if(value.test(o[property])){
27437                         data.push(o);
27438                     }
27439                 }
27440             } else{
27441                 return;
27442             }
27443             this.jsonData = data;
27444             this.refresh();
27445         }
27446     },
27447
27448 /**
27449  * Filter by a function. The passed function will be called with each
27450  * object in the current dataset. If the function returns true the value is kept,
27451  * otherwise it is filtered.
27452  * @param {Function} fn
27453  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27454  */
27455     filterBy : function(fn, scope){
27456         if(this.jsonData){
27457             var data = [];
27458             var ss = this.snapshot;
27459             for(var i = 0, len = ss.length; i < len; i++){
27460                 var o = ss[i];
27461                 if(fn.call(scope || this, o)){
27462                     data.push(o);
27463                 }
27464             }
27465             this.jsonData = data;
27466             this.refresh();
27467         }
27468     },
27469
27470 /**
27471  * Clears the current filter.
27472  */
27473     clearFilter : function(){
27474         if(this.snapshot && this.jsonData != this.snapshot){
27475             this.jsonData = this.snapshot;
27476             this.refresh();
27477         }
27478     },
27479
27480
27481 /**
27482  * Sorts the data for this view and refreshes it.
27483  * @param {String} property A property on your JSON objects to sort on
27484  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27485  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27486  */
27487     sort : function(property, dir, sortType){
27488         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27489         if(this.jsonData){
27490             var p = property;
27491             var dsc = dir && dir.toLowerCase() == "desc";
27492             var f = function(o1, o2){
27493                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27494                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27495                 ;
27496                 if(v1 < v2){
27497                     return dsc ? +1 : -1;
27498                 } else if(v1 > v2){
27499                     return dsc ? -1 : +1;
27500                 } else{
27501                     return 0;
27502                 }
27503             };
27504             this.jsonData.sort(f);
27505             this.refresh();
27506             if(this.jsonData != this.snapshot){
27507                 this.snapshot.sort(f);
27508             }
27509         }
27510     }
27511 });/*
27512  * Based on:
27513  * Ext JS Library 1.1.1
27514  * Copyright(c) 2006-2007, Ext JS, LLC.
27515  *
27516  * Originally Released Under LGPL - original licence link has changed is not relivant.
27517  *
27518  * Fork - LGPL
27519  * <script type="text/javascript">
27520  */
27521  
27522
27523 /**
27524  * @class Roo.ColorPalette
27525  * @extends Roo.Component
27526  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27527  * Here's an example of typical usage:
27528  * <pre><code>
27529 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27530 cp.render('my-div');
27531
27532 cp.on('select', function(palette, selColor){
27533     // do something with selColor
27534 });
27535 </code></pre>
27536  * @constructor
27537  * Create a new ColorPalette
27538  * @param {Object} config The config object
27539  */
27540 Roo.ColorPalette = function(config){
27541     Roo.ColorPalette.superclass.constructor.call(this, config);
27542     this.addEvents({
27543         /**
27544              * @event select
27545              * Fires when a color is selected
27546              * @param {ColorPalette} this
27547              * @param {String} color The 6-digit color hex code (without the # symbol)
27548              */
27549         select: true
27550     });
27551
27552     if(this.handler){
27553         this.on("select", this.handler, this.scope, true);
27554     }
27555 };
27556 Roo.extend(Roo.ColorPalette, Roo.Component, {
27557     /**
27558      * @cfg {String} itemCls
27559      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27560      */
27561     itemCls : "x-color-palette",
27562     /**
27563      * @cfg {String} value
27564      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27565      * the hex codes are case-sensitive.
27566      */
27567     value : null,
27568     clickEvent:'click',
27569     // private
27570     ctype: "Roo.ColorPalette",
27571
27572     /**
27573      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27574      */
27575     allowReselect : false,
27576
27577     /**
27578      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27579      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27580      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27581      * of colors with the width setting until the box is symmetrical.</p>
27582      * <p>You can override individual colors if needed:</p>
27583      * <pre><code>
27584 var cp = new Roo.ColorPalette();
27585 cp.colors[0] = "FF0000";  // change the first box to red
27586 </code></pre>
27587
27588 Or you can provide a custom array of your own for complete control:
27589 <pre><code>
27590 var cp = new Roo.ColorPalette();
27591 cp.colors = ["000000", "993300", "333300"];
27592 </code></pre>
27593      * @type Array
27594      */
27595     colors : [
27596         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27597         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27598         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27599         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27600         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27601     ],
27602
27603     // private
27604     onRender : function(container, position){
27605         var t = new Roo.MasterTemplate(
27606             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27607         );
27608         var c = this.colors;
27609         for(var i = 0, len = c.length; i < len; i++){
27610             t.add([c[i]]);
27611         }
27612         var el = document.createElement("div");
27613         el.className = this.itemCls;
27614         t.overwrite(el);
27615         container.dom.insertBefore(el, position);
27616         this.el = Roo.get(el);
27617         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27618         if(this.clickEvent != 'click'){
27619             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27620         }
27621     },
27622
27623     // private
27624     afterRender : function(){
27625         Roo.ColorPalette.superclass.afterRender.call(this);
27626         if(this.value){
27627             var s = this.value;
27628             this.value = null;
27629             this.select(s);
27630         }
27631     },
27632
27633     // private
27634     handleClick : function(e, t){
27635         e.preventDefault();
27636         if(!this.disabled){
27637             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27638             this.select(c.toUpperCase());
27639         }
27640     },
27641
27642     /**
27643      * Selects the specified color in the palette (fires the select event)
27644      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27645      */
27646     select : function(color){
27647         color = color.replace("#", "");
27648         if(color != this.value || this.allowReselect){
27649             var el = this.el;
27650             if(this.value){
27651                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27652             }
27653             el.child("a.color-"+color).addClass("x-color-palette-sel");
27654             this.value = color;
27655             this.fireEvent("select", this, color);
27656         }
27657     }
27658 });/*
27659  * Based on:
27660  * Ext JS Library 1.1.1
27661  * Copyright(c) 2006-2007, Ext JS, LLC.
27662  *
27663  * Originally Released Under LGPL - original licence link has changed is not relivant.
27664  *
27665  * Fork - LGPL
27666  * <script type="text/javascript">
27667  */
27668  
27669 /**
27670  * @class Roo.DatePicker
27671  * @extends Roo.Component
27672  * Simple date picker class.
27673  * @constructor
27674  * Create a new DatePicker
27675  * @param {Object} config The config object
27676  */
27677 Roo.DatePicker = function(config){
27678     Roo.DatePicker.superclass.constructor.call(this, config);
27679
27680     this.value = config && config.value ?
27681                  config.value.clearTime() : new Date().clearTime();
27682
27683     this.addEvents({
27684         /**
27685              * @event select
27686              * Fires when a date is selected
27687              * @param {DatePicker} this
27688              * @param {Date} date The selected date
27689              */
27690         'select': true,
27691         /**
27692              * @event monthchange
27693              * Fires when the displayed month changes 
27694              * @param {DatePicker} this
27695              * @param {Date} date The selected month
27696              */
27697         'monthchange': true
27698     });
27699
27700     if(this.handler){
27701         this.on("select", this.handler,  this.scope || this);
27702     }
27703     // build the disabledDatesRE
27704     if(!this.disabledDatesRE && this.disabledDates){
27705         var dd = this.disabledDates;
27706         var re = "(?:";
27707         for(var i = 0; i < dd.length; i++){
27708             re += dd[i];
27709             if(i != dd.length-1) {
27710                 re += "|";
27711             }
27712         }
27713         this.disabledDatesRE = new RegExp(re + ")");
27714     }
27715 };
27716
27717 Roo.extend(Roo.DatePicker, Roo.Component, {
27718     /**
27719      * @cfg {String} todayText
27720      * The text to display on the button that selects the current date (defaults to "Today")
27721      */
27722     todayText : "Today",
27723     /**
27724      * @cfg {String} okText
27725      * The text to display on the ok button
27726      */
27727     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27728     /**
27729      * @cfg {String} cancelText
27730      * The text to display on the cancel button
27731      */
27732     cancelText : "Cancel",
27733     /**
27734      * @cfg {String} todayTip
27735      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27736      */
27737     todayTip : "{0} (Spacebar)",
27738     /**
27739      * @cfg {Date} minDate
27740      * Minimum allowable date (JavaScript date object, defaults to null)
27741      */
27742     minDate : null,
27743     /**
27744      * @cfg {Date} maxDate
27745      * Maximum allowable date (JavaScript date object, defaults to null)
27746      */
27747     maxDate : null,
27748     /**
27749      * @cfg {String} minText
27750      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27751      */
27752     minText : "This date is before the minimum date",
27753     /**
27754      * @cfg {String} maxText
27755      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27756      */
27757     maxText : "This date is after the maximum date",
27758     /**
27759      * @cfg {String} format
27760      * The default date format string which can be overriden for localization support.  The format must be
27761      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27762      */
27763     format : "m/d/y",
27764     /**
27765      * @cfg {Array} disabledDays
27766      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27767      */
27768     disabledDays : null,
27769     /**
27770      * @cfg {String} disabledDaysText
27771      * The tooltip to display when the date falls on a disabled day (defaults to "")
27772      */
27773     disabledDaysText : "",
27774     /**
27775      * @cfg {RegExp} disabledDatesRE
27776      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27777      */
27778     disabledDatesRE : null,
27779     /**
27780      * @cfg {String} disabledDatesText
27781      * The tooltip text to display when the date falls on a disabled date (defaults to "")
27782      */
27783     disabledDatesText : "",
27784     /**
27785      * @cfg {Boolean} constrainToViewport
27786      * True to constrain the date picker to the viewport (defaults to true)
27787      */
27788     constrainToViewport : true,
27789     /**
27790      * @cfg {Array} monthNames
27791      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
27792      */
27793     monthNames : Date.monthNames,
27794     /**
27795      * @cfg {Array} dayNames
27796      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
27797      */
27798     dayNames : Date.dayNames,
27799     /**
27800      * @cfg {String} nextText
27801      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
27802      */
27803     nextText: 'Next Month (Control+Right)',
27804     /**
27805      * @cfg {String} prevText
27806      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
27807      */
27808     prevText: 'Previous Month (Control+Left)',
27809     /**
27810      * @cfg {String} monthYearText
27811      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
27812      */
27813     monthYearText: 'Choose a month (Control+Up/Down to move years)',
27814     /**
27815      * @cfg {Number} startDay
27816      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
27817      */
27818     startDay : 0,
27819     /**
27820      * @cfg {Bool} showClear
27821      * Show a clear button (usefull for date form elements that can be blank.)
27822      */
27823     
27824     showClear: false,
27825     
27826     /**
27827      * Sets the value of the date field
27828      * @param {Date} value The date to set
27829      */
27830     setValue : function(value){
27831         var old = this.value;
27832         
27833         if (typeof(value) == 'string') {
27834          
27835             value = Date.parseDate(value, this.format);
27836         }
27837         if (!value) {
27838             value = new Date();
27839         }
27840         
27841         this.value = value.clearTime(true);
27842         if(this.el){
27843             this.update(this.value);
27844         }
27845     },
27846
27847     /**
27848      * Gets the current selected value of the date field
27849      * @return {Date} The selected date
27850      */
27851     getValue : function(){
27852         return this.value;
27853     },
27854
27855     // private
27856     focus : function(){
27857         if(this.el){
27858             this.update(this.activeDate);
27859         }
27860     },
27861
27862     // privateval
27863     onRender : function(container, position){
27864         
27865         var m = [
27866              '<table cellspacing="0">',
27867                 '<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>',
27868                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
27869         var dn = this.dayNames;
27870         for(var i = 0; i < 7; i++){
27871             var d = this.startDay+i;
27872             if(d > 6){
27873                 d = d-7;
27874             }
27875             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
27876         }
27877         m[m.length] = "</tr></thead><tbody><tr>";
27878         for(var i = 0; i < 42; i++) {
27879             if(i % 7 == 0 && i != 0){
27880                 m[m.length] = "</tr><tr>";
27881             }
27882             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
27883         }
27884         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
27885             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
27886
27887         var el = document.createElement("div");
27888         el.className = "x-date-picker";
27889         el.innerHTML = m.join("");
27890
27891         container.dom.insertBefore(el, position);
27892
27893         this.el = Roo.get(el);
27894         this.eventEl = Roo.get(el.firstChild);
27895
27896         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
27897             handler: this.showPrevMonth,
27898             scope: this,
27899             preventDefault:true,
27900             stopDefault:true
27901         });
27902
27903         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
27904             handler: this.showNextMonth,
27905             scope: this,
27906             preventDefault:true,
27907             stopDefault:true
27908         });
27909
27910         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
27911
27912         this.monthPicker = this.el.down('div.x-date-mp');
27913         this.monthPicker.enableDisplayMode('block');
27914         
27915         var kn = new Roo.KeyNav(this.eventEl, {
27916             "left" : function(e){
27917                 e.ctrlKey ?
27918                     this.showPrevMonth() :
27919                     this.update(this.activeDate.add("d", -1));
27920             },
27921
27922             "right" : function(e){
27923                 e.ctrlKey ?
27924                     this.showNextMonth() :
27925                     this.update(this.activeDate.add("d", 1));
27926             },
27927
27928             "up" : function(e){
27929                 e.ctrlKey ?
27930                     this.showNextYear() :
27931                     this.update(this.activeDate.add("d", -7));
27932             },
27933
27934             "down" : function(e){
27935                 e.ctrlKey ?
27936                     this.showPrevYear() :
27937                     this.update(this.activeDate.add("d", 7));
27938             },
27939
27940             "pageUp" : function(e){
27941                 this.showNextMonth();
27942             },
27943
27944             "pageDown" : function(e){
27945                 this.showPrevMonth();
27946             },
27947
27948             "enter" : function(e){
27949                 e.stopPropagation();
27950                 return true;
27951             },
27952
27953             scope : this
27954         });
27955
27956         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
27957
27958         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
27959
27960         this.el.unselectable();
27961         
27962         this.cells = this.el.select("table.x-date-inner tbody td");
27963         this.textNodes = this.el.query("table.x-date-inner tbody span");
27964
27965         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
27966             text: "&#160;",
27967             tooltip: this.monthYearText
27968         });
27969
27970         this.mbtn.on('click', this.showMonthPicker, this);
27971         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
27972
27973
27974         var today = (new Date()).dateFormat(this.format);
27975         
27976         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
27977         if (this.showClear) {
27978             baseTb.add( new Roo.Toolbar.Fill());
27979         }
27980         baseTb.add({
27981             text: String.format(this.todayText, today),
27982             tooltip: String.format(this.todayTip, today),
27983             handler: this.selectToday,
27984             scope: this
27985         });
27986         
27987         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
27988             
27989         //});
27990         if (this.showClear) {
27991             
27992             baseTb.add( new Roo.Toolbar.Fill());
27993             baseTb.add({
27994                 text: '&#160;',
27995                 cls: 'x-btn-icon x-btn-clear',
27996                 handler: function() {
27997                     //this.value = '';
27998                     this.fireEvent("select", this, '');
27999                 },
28000                 scope: this
28001             });
28002         }
28003         
28004         
28005         if(Roo.isIE){
28006             this.el.repaint();
28007         }
28008         this.update(this.value);
28009     },
28010
28011     createMonthPicker : function(){
28012         if(!this.monthPicker.dom.firstChild){
28013             var buf = ['<table border="0" cellspacing="0">'];
28014             for(var i = 0; i < 6; i++){
28015                 buf.push(
28016                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28017                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28018                     i == 0 ?
28019                     '<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>' :
28020                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28021                 );
28022             }
28023             buf.push(
28024                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28025                     this.okText,
28026                     '</button><button type="button" class="x-date-mp-cancel">',
28027                     this.cancelText,
28028                     '</button></td></tr>',
28029                 '</table>'
28030             );
28031             this.monthPicker.update(buf.join(''));
28032             this.monthPicker.on('click', this.onMonthClick, this);
28033             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28034
28035             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28036             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28037
28038             this.mpMonths.each(function(m, a, i){
28039                 i += 1;
28040                 if((i%2) == 0){
28041                     m.dom.xmonth = 5 + Math.round(i * .5);
28042                 }else{
28043                     m.dom.xmonth = Math.round((i-1) * .5);
28044                 }
28045             });
28046         }
28047     },
28048
28049     showMonthPicker : function(){
28050         this.createMonthPicker();
28051         var size = this.el.getSize();
28052         this.monthPicker.setSize(size);
28053         this.monthPicker.child('table').setSize(size);
28054
28055         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28056         this.updateMPMonth(this.mpSelMonth);
28057         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28058         this.updateMPYear(this.mpSelYear);
28059
28060         this.monthPicker.slideIn('t', {duration:.2});
28061     },
28062
28063     updateMPYear : function(y){
28064         this.mpyear = y;
28065         var ys = this.mpYears.elements;
28066         for(var i = 1; i <= 10; i++){
28067             var td = ys[i-1], y2;
28068             if((i%2) == 0){
28069                 y2 = y + Math.round(i * .5);
28070                 td.firstChild.innerHTML = y2;
28071                 td.xyear = y2;
28072             }else{
28073                 y2 = y - (5-Math.round(i * .5));
28074                 td.firstChild.innerHTML = y2;
28075                 td.xyear = y2;
28076             }
28077             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28078         }
28079     },
28080
28081     updateMPMonth : function(sm){
28082         this.mpMonths.each(function(m, a, i){
28083             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28084         });
28085     },
28086
28087     selectMPMonth: function(m){
28088         
28089     },
28090
28091     onMonthClick : function(e, t){
28092         e.stopEvent();
28093         var el = new Roo.Element(t), pn;
28094         if(el.is('button.x-date-mp-cancel')){
28095             this.hideMonthPicker();
28096         }
28097         else if(el.is('button.x-date-mp-ok')){
28098             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28099             this.hideMonthPicker();
28100         }
28101         else if(pn = el.up('td.x-date-mp-month', 2)){
28102             this.mpMonths.removeClass('x-date-mp-sel');
28103             pn.addClass('x-date-mp-sel');
28104             this.mpSelMonth = pn.dom.xmonth;
28105         }
28106         else if(pn = el.up('td.x-date-mp-year', 2)){
28107             this.mpYears.removeClass('x-date-mp-sel');
28108             pn.addClass('x-date-mp-sel');
28109             this.mpSelYear = pn.dom.xyear;
28110         }
28111         else if(el.is('a.x-date-mp-prev')){
28112             this.updateMPYear(this.mpyear-10);
28113         }
28114         else if(el.is('a.x-date-mp-next')){
28115             this.updateMPYear(this.mpyear+10);
28116         }
28117     },
28118
28119     onMonthDblClick : function(e, t){
28120         e.stopEvent();
28121         var el = new Roo.Element(t), pn;
28122         if(pn = el.up('td.x-date-mp-month', 2)){
28123             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28124             this.hideMonthPicker();
28125         }
28126         else if(pn = el.up('td.x-date-mp-year', 2)){
28127             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28128             this.hideMonthPicker();
28129         }
28130     },
28131
28132     hideMonthPicker : function(disableAnim){
28133         if(this.monthPicker){
28134             if(disableAnim === true){
28135                 this.monthPicker.hide();
28136             }else{
28137                 this.monthPicker.slideOut('t', {duration:.2});
28138             }
28139         }
28140     },
28141
28142     // private
28143     showPrevMonth : function(e){
28144         this.update(this.activeDate.add("mo", -1));
28145     },
28146
28147     // private
28148     showNextMonth : function(e){
28149         this.update(this.activeDate.add("mo", 1));
28150     },
28151
28152     // private
28153     showPrevYear : function(){
28154         this.update(this.activeDate.add("y", -1));
28155     },
28156
28157     // private
28158     showNextYear : function(){
28159         this.update(this.activeDate.add("y", 1));
28160     },
28161
28162     // private
28163     handleMouseWheel : function(e){
28164         var delta = e.getWheelDelta();
28165         if(delta > 0){
28166             this.showPrevMonth();
28167             e.stopEvent();
28168         } else if(delta < 0){
28169             this.showNextMonth();
28170             e.stopEvent();
28171         }
28172     },
28173
28174     // private
28175     handleDateClick : function(e, t){
28176         e.stopEvent();
28177         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28178             this.setValue(new Date(t.dateValue));
28179             this.fireEvent("select", this, this.value);
28180         }
28181     },
28182
28183     // private
28184     selectToday : function(){
28185         this.setValue(new Date().clearTime());
28186         this.fireEvent("select", this, this.value);
28187     },
28188
28189     // private
28190     update : function(date)
28191     {
28192         var vd = this.activeDate;
28193         this.activeDate = date;
28194         if(vd && this.el){
28195             var t = date.getTime();
28196             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28197                 this.cells.removeClass("x-date-selected");
28198                 this.cells.each(function(c){
28199                    if(c.dom.firstChild.dateValue == t){
28200                        c.addClass("x-date-selected");
28201                        setTimeout(function(){
28202                             try{c.dom.firstChild.focus();}catch(e){}
28203                        }, 50);
28204                        return false;
28205                    }
28206                 });
28207                 return;
28208             }
28209         }
28210         
28211         var days = date.getDaysInMonth();
28212         var firstOfMonth = date.getFirstDateOfMonth();
28213         var startingPos = firstOfMonth.getDay()-this.startDay;
28214
28215         if(startingPos <= this.startDay){
28216             startingPos += 7;
28217         }
28218
28219         var pm = date.add("mo", -1);
28220         var prevStart = pm.getDaysInMonth()-startingPos;
28221
28222         var cells = this.cells.elements;
28223         var textEls = this.textNodes;
28224         days += startingPos;
28225
28226         // convert everything to numbers so it's fast
28227         var day = 86400000;
28228         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28229         var today = new Date().clearTime().getTime();
28230         var sel = date.clearTime().getTime();
28231         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28232         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28233         var ddMatch = this.disabledDatesRE;
28234         var ddText = this.disabledDatesText;
28235         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28236         var ddaysText = this.disabledDaysText;
28237         var format = this.format;
28238
28239         var setCellClass = function(cal, cell){
28240             cell.title = "";
28241             var t = d.getTime();
28242             cell.firstChild.dateValue = t;
28243             if(t == today){
28244                 cell.className += " x-date-today";
28245                 cell.title = cal.todayText;
28246             }
28247             if(t == sel){
28248                 cell.className += " x-date-selected";
28249                 setTimeout(function(){
28250                     try{cell.firstChild.focus();}catch(e){}
28251                 }, 50);
28252             }
28253             // disabling
28254             if(t < min) {
28255                 cell.className = " x-date-disabled";
28256                 cell.title = cal.minText;
28257                 return;
28258             }
28259             if(t > max) {
28260                 cell.className = " x-date-disabled";
28261                 cell.title = cal.maxText;
28262                 return;
28263             }
28264             if(ddays){
28265                 if(ddays.indexOf(d.getDay()) != -1){
28266                     cell.title = ddaysText;
28267                     cell.className = " x-date-disabled";
28268                 }
28269             }
28270             if(ddMatch && format){
28271                 var fvalue = d.dateFormat(format);
28272                 if(ddMatch.test(fvalue)){
28273                     cell.title = ddText.replace("%0", fvalue);
28274                     cell.className = " x-date-disabled";
28275                 }
28276             }
28277         };
28278
28279         var i = 0;
28280         for(; i < startingPos; i++) {
28281             textEls[i].innerHTML = (++prevStart);
28282             d.setDate(d.getDate()+1);
28283             cells[i].className = "x-date-prevday";
28284             setCellClass(this, cells[i]);
28285         }
28286         for(; i < days; i++){
28287             intDay = i - startingPos + 1;
28288             textEls[i].innerHTML = (intDay);
28289             d.setDate(d.getDate()+1);
28290             cells[i].className = "x-date-active";
28291             setCellClass(this, cells[i]);
28292         }
28293         var extraDays = 0;
28294         for(; i < 42; i++) {
28295              textEls[i].innerHTML = (++extraDays);
28296              d.setDate(d.getDate()+1);
28297              cells[i].className = "x-date-nextday";
28298              setCellClass(this, cells[i]);
28299         }
28300
28301         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28302         this.fireEvent('monthchange', this, date);
28303         
28304         if(!this.internalRender){
28305             var main = this.el.dom.firstChild;
28306             var w = main.offsetWidth;
28307             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28308             Roo.fly(main).setWidth(w);
28309             this.internalRender = true;
28310             // opera does not respect the auto grow header center column
28311             // then, after it gets a width opera refuses to recalculate
28312             // without a second pass
28313             if(Roo.isOpera && !this.secondPass){
28314                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28315                 this.secondPass = true;
28316                 this.update.defer(10, this, [date]);
28317             }
28318         }
28319         
28320         
28321     }
28322 });        /*
28323  * Based on:
28324  * Ext JS Library 1.1.1
28325  * Copyright(c) 2006-2007, Ext JS, LLC.
28326  *
28327  * Originally Released Under LGPL - original licence link has changed is not relivant.
28328  *
28329  * Fork - LGPL
28330  * <script type="text/javascript">
28331  */
28332 /**
28333  * @class Roo.TabPanel
28334  * @extends Roo.util.Observable
28335  * A lightweight tab container.
28336  * <br><br>
28337  * Usage:
28338  * <pre><code>
28339 // basic tabs 1, built from existing content
28340 var tabs = new Roo.TabPanel("tabs1");
28341 tabs.addTab("script", "View Script");
28342 tabs.addTab("markup", "View Markup");
28343 tabs.activate("script");
28344
28345 // more advanced tabs, built from javascript
28346 var jtabs = new Roo.TabPanel("jtabs");
28347 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28348
28349 // set up the UpdateManager
28350 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28351 var updater = tab2.getUpdateManager();
28352 updater.setDefaultUrl("ajax1.htm");
28353 tab2.on('activate', updater.refresh, updater, true);
28354
28355 // Use setUrl for Ajax loading
28356 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28357 tab3.setUrl("ajax2.htm", null, true);
28358
28359 // Disabled tab
28360 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28361 tab4.disable();
28362
28363 jtabs.activate("jtabs-1");
28364  * </code></pre>
28365  * @constructor
28366  * Create a new TabPanel.
28367  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28368  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28369  */
28370 Roo.TabPanel = function(container, config){
28371     /**
28372     * The container element for this TabPanel.
28373     * @type Roo.Element
28374     */
28375     this.el = Roo.get(container, true);
28376     if(config){
28377         if(typeof config == "boolean"){
28378             this.tabPosition = config ? "bottom" : "top";
28379         }else{
28380             Roo.apply(this, config);
28381         }
28382     }
28383     if(this.tabPosition == "bottom"){
28384         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28385         this.el.addClass("x-tabs-bottom");
28386     }
28387     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28388     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28389     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28390     if(Roo.isIE){
28391         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28392     }
28393     if(this.tabPosition != "bottom"){
28394         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28395          * @type Roo.Element
28396          */
28397         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28398         this.el.addClass("x-tabs-top");
28399     }
28400     this.items = [];
28401
28402     this.bodyEl.setStyle("position", "relative");
28403
28404     this.active = null;
28405     this.activateDelegate = this.activate.createDelegate(this);
28406
28407     this.addEvents({
28408         /**
28409          * @event tabchange
28410          * Fires when the active tab changes
28411          * @param {Roo.TabPanel} this
28412          * @param {Roo.TabPanelItem} activePanel The new active tab
28413          */
28414         "tabchange": true,
28415         /**
28416          * @event beforetabchange
28417          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28418          * @param {Roo.TabPanel} this
28419          * @param {Object} e Set cancel to true on this object to cancel the tab change
28420          * @param {Roo.TabPanelItem} tab The tab being changed to
28421          */
28422         "beforetabchange" : true
28423     });
28424
28425     Roo.EventManager.onWindowResize(this.onResize, this);
28426     this.cpad = this.el.getPadding("lr");
28427     this.hiddenCount = 0;
28428
28429
28430     // toolbar on the tabbar support...
28431     if (this.toolbar) {
28432         var tcfg = this.toolbar;
28433         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28434         this.toolbar = new Roo.Toolbar(tcfg);
28435         if (Roo.isSafari) {
28436             var tbl = tcfg.container.child('table', true);
28437             tbl.setAttribute('width', '100%');
28438         }
28439         
28440     }
28441    
28442
28443
28444     Roo.TabPanel.superclass.constructor.call(this);
28445 };
28446
28447 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28448     /*
28449      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28450      */
28451     tabPosition : "top",
28452     /*
28453      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28454      */
28455     currentTabWidth : 0,
28456     /*
28457      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28458      */
28459     minTabWidth : 40,
28460     /*
28461      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28462      */
28463     maxTabWidth : 250,
28464     /*
28465      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28466      */
28467     preferredTabWidth : 175,
28468     /*
28469      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28470      */
28471     resizeTabs : false,
28472     /*
28473      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28474      */
28475     monitorResize : true,
28476     /*
28477      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28478      */
28479     toolbar : false,
28480
28481     /**
28482      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28483      * @param {String} id The id of the div to use <b>or create</b>
28484      * @param {String} text The text for the tab
28485      * @param {String} content (optional) Content to put in the TabPanelItem body
28486      * @param {Boolean} closable (optional) True to create a close icon on the tab
28487      * @return {Roo.TabPanelItem} The created TabPanelItem
28488      */
28489     addTab : function(id, text, content, closable){
28490         var item = new Roo.TabPanelItem(this, id, text, closable);
28491         this.addTabItem(item);
28492         if(content){
28493             item.setContent(content);
28494         }
28495         return item;
28496     },
28497
28498     /**
28499      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28500      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28501      * @return {Roo.TabPanelItem}
28502      */
28503     getTab : function(id){
28504         return this.items[id];
28505     },
28506
28507     /**
28508      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28509      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28510      */
28511     hideTab : function(id){
28512         var t = this.items[id];
28513         if(!t.isHidden()){
28514            t.setHidden(true);
28515            this.hiddenCount++;
28516            this.autoSizeTabs();
28517         }
28518     },
28519
28520     /**
28521      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28522      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28523      */
28524     unhideTab : function(id){
28525         var t = this.items[id];
28526         if(t.isHidden()){
28527            t.setHidden(false);
28528            this.hiddenCount--;
28529            this.autoSizeTabs();
28530         }
28531     },
28532
28533     /**
28534      * Adds an existing {@link Roo.TabPanelItem}.
28535      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28536      */
28537     addTabItem : function(item){
28538         this.items[item.id] = item;
28539         this.items.push(item);
28540         if(this.resizeTabs){
28541            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28542            this.autoSizeTabs();
28543         }else{
28544             item.autoSize();
28545         }
28546     },
28547
28548     /**
28549      * Removes a {@link Roo.TabPanelItem}.
28550      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28551      */
28552     removeTab : function(id){
28553         var items = this.items;
28554         var tab = items[id];
28555         if(!tab) { return; }
28556         var index = items.indexOf(tab);
28557         if(this.active == tab && items.length > 1){
28558             var newTab = this.getNextAvailable(index);
28559             if(newTab) {
28560                 newTab.activate();
28561             }
28562         }
28563         this.stripEl.dom.removeChild(tab.pnode.dom);
28564         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28565             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28566         }
28567         items.splice(index, 1);
28568         delete this.items[tab.id];
28569         tab.fireEvent("close", tab);
28570         tab.purgeListeners();
28571         this.autoSizeTabs();
28572     },
28573
28574     getNextAvailable : function(start){
28575         var items = this.items;
28576         var index = start;
28577         // look for a next tab that will slide over to
28578         // replace the one being removed
28579         while(index < items.length){
28580             var item = items[++index];
28581             if(item && !item.isHidden()){
28582                 return item;
28583             }
28584         }
28585         // if one isn't found select the previous tab (on the left)
28586         index = start;
28587         while(index >= 0){
28588             var item = items[--index];
28589             if(item && !item.isHidden()){
28590                 return item;
28591             }
28592         }
28593         return null;
28594     },
28595
28596     /**
28597      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28598      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28599      */
28600     disableTab : function(id){
28601         var tab = this.items[id];
28602         if(tab && this.active != tab){
28603             tab.disable();
28604         }
28605     },
28606
28607     /**
28608      * Enables a {@link Roo.TabPanelItem} that is disabled.
28609      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28610      */
28611     enableTab : function(id){
28612         var tab = this.items[id];
28613         tab.enable();
28614     },
28615
28616     /**
28617      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28618      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28619      * @return {Roo.TabPanelItem} The TabPanelItem.
28620      */
28621     activate : function(id){
28622         var tab = this.items[id];
28623         if(!tab){
28624             return null;
28625         }
28626         if(tab == this.active || tab.disabled){
28627             return tab;
28628         }
28629         var e = {};
28630         this.fireEvent("beforetabchange", this, e, tab);
28631         if(e.cancel !== true && !tab.disabled){
28632             if(this.active){
28633                 this.active.hide();
28634             }
28635             this.active = this.items[id];
28636             this.active.show();
28637             this.fireEvent("tabchange", this, this.active);
28638         }
28639         return tab;
28640     },
28641
28642     /**
28643      * Gets the active {@link Roo.TabPanelItem}.
28644      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28645      */
28646     getActiveTab : function(){
28647         return this.active;
28648     },
28649
28650     /**
28651      * Updates the tab body element to fit the height of the container element
28652      * for overflow scrolling
28653      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28654      */
28655     syncHeight : function(targetHeight){
28656         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28657         var bm = this.bodyEl.getMargins();
28658         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28659         this.bodyEl.setHeight(newHeight);
28660         return newHeight;
28661     },
28662
28663     onResize : function(){
28664         if(this.monitorResize){
28665             this.autoSizeTabs();
28666         }
28667     },
28668
28669     /**
28670      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28671      */
28672     beginUpdate : function(){
28673         this.updating = true;
28674     },
28675
28676     /**
28677      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28678      */
28679     endUpdate : function(){
28680         this.updating = false;
28681         this.autoSizeTabs();
28682     },
28683
28684     /**
28685      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28686      */
28687     autoSizeTabs : function(){
28688         var count = this.items.length;
28689         var vcount = count - this.hiddenCount;
28690         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28691             return;
28692         }
28693         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28694         var availWidth = Math.floor(w / vcount);
28695         var b = this.stripBody;
28696         if(b.getWidth() > w){
28697             var tabs = this.items;
28698             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28699             if(availWidth < this.minTabWidth){
28700                 /*if(!this.sleft){    // incomplete scrolling code
28701                     this.createScrollButtons();
28702                 }
28703                 this.showScroll();
28704                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28705             }
28706         }else{
28707             if(this.currentTabWidth < this.preferredTabWidth){
28708                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28709             }
28710         }
28711     },
28712
28713     /**
28714      * Returns the number of tabs in this TabPanel.
28715      * @return {Number}
28716      */
28717      getCount : function(){
28718          return this.items.length;
28719      },
28720
28721     /**
28722      * Resizes all the tabs to the passed width
28723      * @param {Number} The new width
28724      */
28725     setTabWidth : function(width){
28726         this.currentTabWidth = width;
28727         for(var i = 0, len = this.items.length; i < len; i++) {
28728                 if(!this.items[i].isHidden()) {
28729                 this.items[i].setWidth(width);
28730             }
28731         }
28732     },
28733
28734     /**
28735      * Destroys this TabPanel
28736      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28737      */
28738     destroy : function(removeEl){
28739         Roo.EventManager.removeResizeListener(this.onResize, this);
28740         for(var i = 0, len = this.items.length; i < len; i++){
28741             this.items[i].purgeListeners();
28742         }
28743         if(removeEl === true){
28744             this.el.update("");
28745             this.el.remove();
28746         }
28747     }
28748 });
28749
28750 /**
28751  * @class Roo.TabPanelItem
28752  * @extends Roo.util.Observable
28753  * Represents an individual item (tab plus body) in a TabPanel.
28754  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28755  * @param {String} id The id of this TabPanelItem
28756  * @param {String} text The text for the tab of this TabPanelItem
28757  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28758  */
28759 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28760     /**
28761      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28762      * @type Roo.TabPanel
28763      */
28764     this.tabPanel = tabPanel;
28765     /**
28766      * The id for this TabPanelItem
28767      * @type String
28768      */
28769     this.id = id;
28770     /** @private */
28771     this.disabled = false;
28772     /** @private */
28773     this.text = text;
28774     /** @private */
28775     this.loaded = false;
28776     this.closable = closable;
28777
28778     /**
28779      * The body element for this TabPanelItem.
28780      * @type Roo.Element
28781      */
28782     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
28783     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
28784     this.bodyEl.setStyle("display", "block");
28785     this.bodyEl.setStyle("zoom", "1");
28786     this.hideAction();
28787
28788     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
28789     /** @private */
28790     this.el = Roo.get(els.el, true);
28791     this.inner = Roo.get(els.inner, true);
28792     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
28793     this.pnode = Roo.get(els.el.parentNode, true);
28794     this.el.on("mousedown", this.onTabMouseDown, this);
28795     this.el.on("click", this.onTabClick, this);
28796     /** @private */
28797     if(closable){
28798         var c = Roo.get(els.close, true);
28799         c.dom.title = this.closeText;
28800         c.addClassOnOver("close-over");
28801         c.on("click", this.closeClick, this);
28802      }
28803
28804     this.addEvents({
28805          /**
28806          * @event activate
28807          * Fires when this tab becomes the active tab.
28808          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28809          * @param {Roo.TabPanelItem} this
28810          */
28811         "activate": true,
28812         /**
28813          * @event beforeclose
28814          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
28815          * @param {Roo.TabPanelItem} this
28816          * @param {Object} e Set cancel to true on this object to cancel the close.
28817          */
28818         "beforeclose": true,
28819         /**
28820          * @event close
28821          * Fires when this tab is closed.
28822          * @param {Roo.TabPanelItem} this
28823          */
28824          "close": true,
28825         /**
28826          * @event deactivate
28827          * Fires when this tab is no longer the active tab.
28828          * @param {Roo.TabPanel} tabPanel The parent TabPanel
28829          * @param {Roo.TabPanelItem} this
28830          */
28831          "deactivate" : true
28832     });
28833     this.hidden = false;
28834
28835     Roo.TabPanelItem.superclass.constructor.call(this);
28836 };
28837
28838 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
28839     purgeListeners : function(){
28840        Roo.util.Observable.prototype.purgeListeners.call(this);
28841        this.el.removeAllListeners();
28842     },
28843     /**
28844      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
28845      */
28846     show : function(){
28847         this.pnode.addClass("on");
28848         this.showAction();
28849         if(Roo.isOpera){
28850             this.tabPanel.stripWrap.repaint();
28851         }
28852         this.fireEvent("activate", this.tabPanel, this);
28853     },
28854
28855     /**
28856      * Returns true if this tab is the active tab.
28857      * @return {Boolean}
28858      */
28859     isActive : function(){
28860         return this.tabPanel.getActiveTab() == this;
28861     },
28862
28863     /**
28864      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
28865      */
28866     hide : function(){
28867         this.pnode.removeClass("on");
28868         this.hideAction();
28869         this.fireEvent("deactivate", this.tabPanel, this);
28870     },
28871
28872     hideAction : function(){
28873         this.bodyEl.hide();
28874         this.bodyEl.setStyle("position", "absolute");
28875         this.bodyEl.setLeft("-20000px");
28876         this.bodyEl.setTop("-20000px");
28877     },
28878
28879     showAction : function(){
28880         this.bodyEl.setStyle("position", "relative");
28881         this.bodyEl.setTop("");
28882         this.bodyEl.setLeft("");
28883         this.bodyEl.show();
28884     },
28885
28886     /**
28887      * Set the tooltip for the tab.
28888      * @param {String} tooltip The tab's tooltip
28889      */
28890     setTooltip : function(text){
28891         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
28892             this.textEl.dom.qtip = text;
28893             this.textEl.dom.removeAttribute('title');
28894         }else{
28895             this.textEl.dom.title = text;
28896         }
28897     },
28898
28899     onTabClick : function(e){
28900         e.preventDefault();
28901         this.tabPanel.activate(this.id);
28902     },
28903
28904     onTabMouseDown : function(e){
28905         e.preventDefault();
28906         this.tabPanel.activate(this.id);
28907     },
28908
28909     getWidth : function(){
28910         return this.inner.getWidth();
28911     },
28912
28913     setWidth : function(width){
28914         var iwidth = width - this.pnode.getPadding("lr");
28915         this.inner.setWidth(iwidth);
28916         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
28917         this.pnode.setWidth(width);
28918     },
28919
28920     /**
28921      * Show or hide the tab
28922      * @param {Boolean} hidden True to hide or false to show.
28923      */
28924     setHidden : function(hidden){
28925         this.hidden = hidden;
28926         this.pnode.setStyle("display", hidden ? "none" : "");
28927     },
28928
28929     /**
28930      * Returns true if this tab is "hidden"
28931      * @return {Boolean}
28932      */
28933     isHidden : function(){
28934         return this.hidden;
28935     },
28936
28937     /**
28938      * Returns the text for this tab
28939      * @return {String}
28940      */
28941     getText : function(){
28942         return this.text;
28943     },
28944
28945     autoSize : function(){
28946         //this.el.beginMeasure();
28947         this.textEl.setWidth(1);
28948         /*
28949          *  #2804 [new] Tabs in Roojs
28950          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
28951          */
28952         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
28953         //this.el.endMeasure();
28954     },
28955
28956     /**
28957      * Sets the text for the tab (Note: this also sets the tooltip text)
28958      * @param {String} text The tab's text and tooltip
28959      */
28960     setText : function(text){
28961         this.text = text;
28962         this.textEl.update(text);
28963         this.setTooltip(text);
28964         if(!this.tabPanel.resizeTabs){
28965             this.autoSize();
28966         }
28967     },
28968     /**
28969      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
28970      */
28971     activate : function(){
28972         this.tabPanel.activate(this.id);
28973     },
28974
28975     /**
28976      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
28977      */
28978     disable : function(){
28979         if(this.tabPanel.active != this){
28980             this.disabled = true;
28981             this.pnode.addClass("disabled");
28982         }
28983     },
28984
28985     /**
28986      * Enables this TabPanelItem if it was previously disabled.
28987      */
28988     enable : function(){
28989         this.disabled = false;
28990         this.pnode.removeClass("disabled");
28991     },
28992
28993     /**
28994      * Sets the content for this TabPanelItem.
28995      * @param {String} content The content
28996      * @param {Boolean} loadScripts true to look for and load scripts
28997      */
28998     setContent : function(content, loadScripts){
28999         this.bodyEl.update(content, loadScripts);
29000     },
29001
29002     /**
29003      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29004      * @return {Roo.UpdateManager} The UpdateManager
29005      */
29006     getUpdateManager : function(){
29007         return this.bodyEl.getUpdateManager();
29008     },
29009
29010     /**
29011      * Set a URL to be used to load the content for this TabPanelItem.
29012      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29013      * @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)
29014      * @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)
29015      * @return {Roo.UpdateManager} The UpdateManager
29016      */
29017     setUrl : function(url, params, loadOnce){
29018         if(this.refreshDelegate){
29019             this.un('activate', this.refreshDelegate);
29020         }
29021         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29022         this.on("activate", this.refreshDelegate);
29023         return this.bodyEl.getUpdateManager();
29024     },
29025
29026     /** @private */
29027     _handleRefresh : function(url, params, loadOnce){
29028         if(!loadOnce || !this.loaded){
29029             var updater = this.bodyEl.getUpdateManager();
29030             updater.update(url, params, this._setLoaded.createDelegate(this));
29031         }
29032     },
29033
29034     /**
29035      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29036      *   Will fail silently if the setUrl method has not been called.
29037      *   This does not activate the panel, just updates its content.
29038      */
29039     refresh : function(){
29040         if(this.refreshDelegate){
29041            this.loaded = false;
29042            this.refreshDelegate();
29043         }
29044     },
29045
29046     /** @private */
29047     _setLoaded : function(){
29048         this.loaded = true;
29049     },
29050
29051     /** @private */
29052     closeClick : function(e){
29053         var o = {};
29054         e.stopEvent();
29055         this.fireEvent("beforeclose", this, o);
29056         if(o.cancel !== true){
29057             this.tabPanel.removeTab(this.id);
29058         }
29059     },
29060     /**
29061      * The text displayed in the tooltip for the close icon.
29062      * @type String
29063      */
29064     closeText : "Close this tab"
29065 });
29066
29067 /** @private */
29068 Roo.TabPanel.prototype.createStrip = function(container){
29069     var strip = document.createElement("div");
29070     strip.className = "x-tabs-wrap";
29071     container.appendChild(strip);
29072     return strip;
29073 };
29074 /** @private */
29075 Roo.TabPanel.prototype.createStripList = function(strip){
29076     // div wrapper for retard IE
29077     // returns the "tr" element.
29078     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29079         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29080         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29081     return strip.firstChild.firstChild.firstChild.firstChild;
29082 };
29083 /** @private */
29084 Roo.TabPanel.prototype.createBody = function(container){
29085     var body = document.createElement("div");
29086     Roo.id(body, "tab-body");
29087     Roo.fly(body).addClass("x-tabs-body");
29088     container.appendChild(body);
29089     return body;
29090 };
29091 /** @private */
29092 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29093     var body = Roo.getDom(id);
29094     if(!body){
29095         body = document.createElement("div");
29096         body.id = id;
29097     }
29098     Roo.fly(body).addClass("x-tabs-item-body");
29099     bodyEl.insertBefore(body, bodyEl.firstChild);
29100     return body;
29101 };
29102 /** @private */
29103 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29104     var td = document.createElement("td");
29105     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29106     //stripEl.appendChild(td);
29107     if(closable){
29108         td.className = "x-tabs-closable";
29109         if(!this.closeTpl){
29110             this.closeTpl = new Roo.Template(
29111                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29112                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29113                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29114             );
29115         }
29116         var el = this.closeTpl.overwrite(td, {"text": text});
29117         var close = el.getElementsByTagName("div")[0];
29118         var inner = el.getElementsByTagName("em")[0];
29119         return {"el": el, "close": close, "inner": inner};
29120     } else {
29121         if(!this.tabTpl){
29122             this.tabTpl = new Roo.Template(
29123                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29124                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29125             );
29126         }
29127         var el = this.tabTpl.overwrite(td, {"text": text});
29128         var inner = el.getElementsByTagName("em")[0];
29129         return {"el": el, "inner": inner};
29130     }
29131 };/*
29132  * Based on:
29133  * Ext JS Library 1.1.1
29134  * Copyright(c) 2006-2007, Ext JS, LLC.
29135  *
29136  * Originally Released Under LGPL - original licence link has changed is not relivant.
29137  *
29138  * Fork - LGPL
29139  * <script type="text/javascript">
29140  */
29141
29142 /**
29143  * @class Roo.Button
29144  * @extends Roo.util.Observable
29145  * Simple Button class
29146  * @cfg {String} text The button text
29147  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29148  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29149  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29150  * @cfg {Object} scope The scope of the handler
29151  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29152  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29153  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29154  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29155  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29156  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29157    applies if enableToggle = true)
29158  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29159  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29160   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29161  * @constructor
29162  * Create a new button
29163  * @param {Object} config The config object
29164  */
29165 Roo.Button = function(renderTo, config)
29166 {
29167     if (!config) {
29168         config = renderTo;
29169         renderTo = config.renderTo || false;
29170     }
29171     
29172     Roo.apply(this, config);
29173     this.addEvents({
29174         /**
29175              * @event click
29176              * Fires when this button is clicked
29177              * @param {Button} this
29178              * @param {EventObject} e The click event
29179              */
29180             "click" : true,
29181         /**
29182              * @event toggle
29183              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29184              * @param {Button} this
29185              * @param {Boolean} pressed
29186              */
29187             "toggle" : true,
29188         /**
29189              * @event mouseover
29190              * Fires when the mouse hovers over the button
29191              * @param {Button} this
29192              * @param {Event} e The event object
29193              */
29194         'mouseover' : true,
29195         /**
29196              * @event mouseout
29197              * Fires when the mouse exits the button
29198              * @param {Button} this
29199              * @param {Event} e The event object
29200              */
29201         'mouseout': true,
29202          /**
29203              * @event render
29204              * Fires when the button is rendered
29205              * @param {Button} this
29206              */
29207         'render': true
29208     });
29209     if(this.menu){
29210         this.menu = Roo.menu.MenuMgr.get(this.menu);
29211     }
29212     // register listeners first!!  - so render can be captured..
29213     Roo.util.Observable.call(this);
29214     if(renderTo){
29215         this.render(renderTo);
29216     }
29217     
29218   
29219 };
29220
29221 Roo.extend(Roo.Button, Roo.util.Observable, {
29222     /**
29223      * 
29224      */
29225     
29226     /**
29227      * Read-only. True if this button is hidden
29228      * @type Boolean
29229      */
29230     hidden : false,
29231     /**
29232      * Read-only. True if this button is disabled
29233      * @type Boolean
29234      */
29235     disabled : false,
29236     /**
29237      * Read-only. True if this button is pressed (only if enableToggle = true)
29238      * @type Boolean
29239      */
29240     pressed : false,
29241
29242     /**
29243      * @cfg {Number} tabIndex 
29244      * The DOM tabIndex for this button (defaults to undefined)
29245      */
29246     tabIndex : undefined,
29247
29248     /**
29249      * @cfg {Boolean} enableToggle
29250      * True to enable pressed/not pressed toggling (defaults to false)
29251      */
29252     enableToggle: false,
29253     /**
29254      * @cfg {Mixed} menu
29255      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29256      */
29257     menu : undefined,
29258     /**
29259      * @cfg {String} menuAlign
29260      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29261      */
29262     menuAlign : "tl-bl?",
29263
29264     /**
29265      * @cfg {String} iconCls
29266      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29267      */
29268     iconCls : undefined,
29269     /**
29270      * @cfg {String} type
29271      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29272      */
29273     type : 'button',
29274
29275     // private
29276     menuClassTarget: 'tr',
29277
29278     /**
29279      * @cfg {String} clickEvent
29280      * The type of event to map to the button's event handler (defaults to 'click')
29281      */
29282     clickEvent : 'click',
29283
29284     /**
29285      * @cfg {Boolean} handleMouseEvents
29286      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29287      */
29288     handleMouseEvents : true,
29289
29290     /**
29291      * @cfg {String} tooltipType
29292      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29293      */
29294     tooltipType : 'qtip',
29295
29296     /**
29297      * @cfg {String} cls
29298      * A CSS class to apply to the button's main element.
29299      */
29300     
29301     /**
29302      * @cfg {Roo.Template} template (Optional)
29303      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29304      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29305      * require code modifications if required elements (e.g. a button) aren't present.
29306      */
29307
29308     // private
29309     render : function(renderTo){
29310         var btn;
29311         if(this.hideParent){
29312             this.parentEl = Roo.get(renderTo);
29313         }
29314         if(!this.dhconfig){
29315             if(!this.template){
29316                 if(!Roo.Button.buttonTemplate){
29317                     // hideous table template
29318                     Roo.Button.buttonTemplate = new Roo.Template(
29319                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29320                         '<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>',
29321                         "</tr></tbody></table>");
29322                 }
29323                 this.template = Roo.Button.buttonTemplate;
29324             }
29325             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29326             var btnEl = btn.child("button:first");
29327             btnEl.on('focus', this.onFocus, this);
29328             btnEl.on('blur', this.onBlur, this);
29329             if(this.cls){
29330                 btn.addClass(this.cls);
29331             }
29332             if(this.icon){
29333                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29334             }
29335             if(this.iconCls){
29336                 btnEl.addClass(this.iconCls);
29337                 if(!this.cls){
29338                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29339                 }
29340             }
29341             if(this.tabIndex !== undefined){
29342                 btnEl.dom.tabIndex = this.tabIndex;
29343             }
29344             if(this.tooltip){
29345                 if(typeof this.tooltip == 'object'){
29346                     Roo.QuickTips.tips(Roo.apply({
29347                           target: btnEl.id
29348                     }, this.tooltip));
29349                 } else {
29350                     btnEl.dom[this.tooltipType] = this.tooltip;
29351                 }
29352             }
29353         }else{
29354             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29355         }
29356         this.el = btn;
29357         if(this.id){
29358             this.el.dom.id = this.el.id = this.id;
29359         }
29360         if(this.menu){
29361             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29362             this.menu.on("show", this.onMenuShow, this);
29363             this.menu.on("hide", this.onMenuHide, this);
29364         }
29365         btn.addClass("x-btn");
29366         if(Roo.isIE && !Roo.isIE7){
29367             this.autoWidth.defer(1, this);
29368         }else{
29369             this.autoWidth();
29370         }
29371         if(this.handleMouseEvents){
29372             btn.on("mouseover", this.onMouseOver, this);
29373             btn.on("mouseout", this.onMouseOut, this);
29374             btn.on("mousedown", this.onMouseDown, this);
29375         }
29376         btn.on(this.clickEvent, this.onClick, this);
29377         //btn.on("mouseup", this.onMouseUp, this);
29378         if(this.hidden){
29379             this.hide();
29380         }
29381         if(this.disabled){
29382             this.disable();
29383         }
29384         Roo.ButtonToggleMgr.register(this);
29385         if(this.pressed){
29386             this.el.addClass("x-btn-pressed");
29387         }
29388         if(this.repeat){
29389             var repeater = new Roo.util.ClickRepeater(btn,
29390                 typeof this.repeat == "object" ? this.repeat : {}
29391             );
29392             repeater.on("click", this.onClick,  this);
29393         }
29394         
29395         this.fireEvent('render', this);
29396         
29397     },
29398     /**
29399      * Returns the button's underlying element
29400      * @return {Roo.Element} The element
29401      */
29402     getEl : function(){
29403         return this.el;  
29404     },
29405     
29406     /**
29407      * Destroys this Button and removes any listeners.
29408      */
29409     destroy : function(){
29410         Roo.ButtonToggleMgr.unregister(this);
29411         this.el.removeAllListeners();
29412         this.purgeListeners();
29413         this.el.remove();
29414     },
29415
29416     // private
29417     autoWidth : function(){
29418         if(this.el){
29419             this.el.setWidth("auto");
29420             if(Roo.isIE7 && Roo.isStrict){
29421                 var ib = this.el.child('button');
29422                 if(ib && ib.getWidth() > 20){
29423                     ib.clip();
29424                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29425                 }
29426             }
29427             if(this.minWidth){
29428                 if(this.hidden){
29429                     this.el.beginMeasure();
29430                 }
29431                 if(this.el.getWidth() < this.minWidth){
29432                     this.el.setWidth(this.minWidth);
29433                 }
29434                 if(this.hidden){
29435                     this.el.endMeasure();
29436                 }
29437             }
29438         }
29439     },
29440
29441     /**
29442      * Assigns this button's click handler
29443      * @param {Function} handler The function to call when the button is clicked
29444      * @param {Object} scope (optional) Scope for the function passed in
29445      */
29446     setHandler : function(handler, scope){
29447         this.handler = handler;
29448         this.scope = scope;  
29449     },
29450     
29451     /**
29452      * Sets this button's text
29453      * @param {String} text The button text
29454      */
29455     setText : function(text){
29456         this.text = text;
29457         if(this.el){
29458             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29459         }
29460         this.autoWidth();
29461     },
29462     
29463     /**
29464      * Gets the text for this button
29465      * @return {String} The button text
29466      */
29467     getText : function(){
29468         return this.text;  
29469     },
29470     
29471     /**
29472      * Show this button
29473      */
29474     show: function(){
29475         this.hidden = false;
29476         if(this.el){
29477             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29478         }
29479     },
29480     
29481     /**
29482      * Hide this button
29483      */
29484     hide: function(){
29485         this.hidden = true;
29486         if(this.el){
29487             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29488         }
29489     },
29490     
29491     /**
29492      * Convenience function for boolean show/hide
29493      * @param {Boolean} visible True to show, false to hide
29494      */
29495     setVisible: function(visible){
29496         if(visible) {
29497             this.show();
29498         }else{
29499             this.hide();
29500         }
29501     },
29502     
29503     /**
29504      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29505      * @param {Boolean} state (optional) Force a particular state
29506      */
29507     toggle : function(state){
29508         state = state === undefined ? !this.pressed : state;
29509         if(state != this.pressed){
29510             if(state){
29511                 this.el.addClass("x-btn-pressed");
29512                 this.pressed = true;
29513                 this.fireEvent("toggle", this, true);
29514             }else{
29515                 this.el.removeClass("x-btn-pressed");
29516                 this.pressed = false;
29517                 this.fireEvent("toggle", this, false);
29518             }
29519             if(this.toggleHandler){
29520                 this.toggleHandler.call(this.scope || this, this, state);
29521             }
29522         }
29523     },
29524     
29525     /**
29526      * Focus the button
29527      */
29528     focus : function(){
29529         this.el.child('button:first').focus();
29530     },
29531     
29532     /**
29533      * Disable this button
29534      */
29535     disable : function(){
29536         if(this.el){
29537             this.el.addClass("x-btn-disabled");
29538         }
29539         this.disabled = true;
29540     },
29541     
29542     /**
29543      * Enable this button
29544      */
29545     enable : function(){
29546         if(this.el){
29547             this.el.removeClass("x-btn-disabled");
29548         }
29549         this.disabled = false;
29550     },
29551
29552     /**
29553      * Convenience function for boolean enable/disable
29554      * @param {Boolean} enabled True to enable, false to disable
29555      */
29556     setDisabled : function(v){
29557         this[v !== true ? "enable" : "disable"]();
29558     },
29559
29560     // private
29561     onClick : function(e)
29562     {
29563         if(e){
29564             e.preventDefault();
29565         }
29566         if(e.button != 0){
29567             return;
29568         }
29569         if(!this.disabled){
29570             if(this.enableToggle){
29571                 this.toggle();
29572             }
29573             if(this.menu && !this.menu.isVisible()){
29574                 this.menu.show(this.el, this.menuAlign);
29575             }
29576             this.fireEvent("click", this, e);
29577             if(this.handler){
29578                 this.el.removeClass("x-btn-over");
29579                 this.handler.call(this.scope || this, this, e);
29580             }
29581         }
29582     },
29583     // private
29584     onMouseOver : function(e){
29585         if(!this.disabled){
29586             this.el.addClass("x-btn-over");
29587             this.fireEvent('mouseover', this, e);
29588         }
29589     },
29590     // private
29591     onMouseOut : function(e){
29592         if(!e.within(this.el,  true)){
29593             this.el.removeClass("x-btn-over");
29594             this.fireEvent('mouseout', this, e);
29595         }
29596     },
29597     // private
29598     onFocus : function(e){
29599         if(!this.disabled){
29600             this.el.addClass("x-btn-focus");
29601         }
29602     },
29603     // private
29604     onBlur : function(e){
29605         this.el.removeClass("x-btn-focus");
29606     },
29607     // private
29608     onMouseDown : function(e){
29609         if(!this.disabled && e.button == 0){
29610             this.el.addClass("x-btn-click");
29611             Roo.get(document).on('mouseup', this.onMouseUp, this);
29612         }
29613     },
29614     // private
29615     onMouseUp : function(e){
29616         if(e.button == 0){
29617             this.el.removeClass("x-btn-click");
29618             Roo.get(document).un('mouseup', this.onMouseUp, this);
29619         }
29620     },
29621     // private
29622     onMenuShow : function(e){
29623         this.el.addClass("x-btn-menu-active");
29624     },
29625     // private
29626     onMenuHide : function(e){
29627         this.el.removeClass("x-btn-menu-active");
29628     }   
29629 });
29630
29631 // Private utility class used by Button
29632 Roo.ButtonToggleMgr = function(){
29633    var groups = {};
29634    
29635    function toggleGroup(btn, state){
29636        if(state){
29637            var g = groups[btn.toggleGroup];
29638            for(var i = 0, l = g.length; i < l; i++){
29639                if(g[i] != btn){
29640                    g[i].toggle(false);
29641                }
29642            }
29643        }
29644    }
29645    
29646    return {
29647        register : function(btn){
29648            if(!btn.toggleGroup){
29649                return;
29650            }
29651            var g = groups[btn.toggleGroup];
29652            if(!g){
29653                g = groups[btn.toggleGroup] = [];
29654            }
29655            g.push(btn);
29656            btn.on("toggle", toggleGroup);
29657        },
29658        
29659        unregister : function(btn){
29660            if(!btn.toggleGroup){
29661                return;
29662            }
29663            var g = groups[btn.toggleGroup];
29664            if(g){
29665                g.remove(btn);
29666                btn.un("toggle", toggleGroup);
29667            }
29668        }
29669    };
29670 }();/*
29671  * Based on:
29672  * Ext JS Library 1.1.1
29673  * Copyright(c) 2006-2007, Ext JS, LLC.
29674  *
29675  * Originally Released Under LGPL - original licence link has changed is not relivant.
29676  *
29677  * Fork - LGPL
29678  * <script type="text/javascript">
29679  */
29680  
29681 /**
29682  * @class Roo.SplitButton
29683  * @extends Roo.Button
29684  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29685  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29686  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29687  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29688  * @cfg {String} arrowTooltip The title attribute of the arrow
29689  * @constructor
29690  * Create a new menu button
29691  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29692  * @param {Object} config The config object
29693  */
29694 Roo.SplitButton = function(renderTo, config){
29695     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29696     /**
29697      * @event arrowclick
29698      * Fires when this button's arrow is clicked
29699      * @param {SplitButton} this
29700      * @param {EventObject} e The click event
29701      */
29702     this.addEvents({"arrowclick":true});
29703 };
29704
29705 Roo.extend(Roo.SplitButton, Roo.Button, {
29706     render : function(renderTo){
29707         // this is one sweet looking template!
29708         var tpl = new Roo.Template(
29709             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29710             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29711             '<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>',
29712             "</tbody></table></td><td>",
29713             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29714             '<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>',
29715             "</tbody></table></td></tr></table>"
29716         );
29717         var btn = tpl.append(renderTo, [this.text, this.type], true);
29718         var btnEl = btn.child("button");
29719         if(this.cls){
29720             btn.addClass(this.cls);
29721         }
29722         if(this.icon){
29723             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29724         }
29725         if(this.iconCls){
29726             btnEl.addClass(this.iconCls);
29727             if(!this.cls){
29728                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29729             }
29730         }
29731         this.el = btn;
29732         if(this.handleMouseEvents){
29733             btn.on("mouseover", this.onMouseOver, this);
29734             btn.on("mouseout", this.onMouseOut, this);
29735             btn.on("mousedown", this.onMouseDown, this);
29736             btn.on("mouseup", this.onMouseUp, this);
29737         }
29738         btn.on(this.clickEvent, this.onClick, this);
29739         if(this.tooltip){
29740             if(typeof this.tooltip == 'object'){
29741                 Roo.QuickTips.tips(Roo.apply({
29742                       target: btnEl.id
29743                 }, this.tooltip));
29744             } else {
29745                 btnEl.dom[this.tooltipType] = this.tooltip;
29746             }
29747         }
29748         if(this.arrowTooltip){
29749             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29750         }
29751         if(this.hidden){
29752             this.hide();
29753         }
29754         if(this.disabled){
29755             this.disable();
29756         }
29757         if(this.pressed){
29758             this.el.addClass("x-btn-pressed");
29759         }
29760         if(Roo.isIE && !Roo.isIE7){
29761             this.autoWidth.defer(1, this);
29762         }else{
29763             this.autoWidth();
29764         }
29765         if(this.menu){
29766             this.menu.on("show", this.onMenuShow, this);
29767             this.menu.on("hide", this.onMenuHide, this);
29768         }
29769         this.fireEvent('render', this);
29770     },
29771
29772     // private
29773     autoWidth : function(){
29774         if(this.el){
29775             var tbl = this.el.child("table:first");
29776             var tbl2 = this.el.child("table:last");
29777             this.el.setWidth("auto");
29778             tbl.setWidth("auto");
29779             if(Roo.isIE7 && Roo.isStrict){
29780                 var ib = this.el.child('button:first');
29781                 if(ib && ib.getWidth() > 20){
29782                     ib.clip();
29783                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29784                 }
29785             }
29786             if(this.minWidth){
29787                 if(this.hidden){
29788                     this.el.beginMeasure();
29789                 }
29790                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
29791                     tbl.setWidth(this.minWidth-tbl2.getWidth());
29792                 }
29793                 if(this.hidden){
29794                     this.el.endMeasure();
29795                 }
29796             }
29797             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
29798         } 
29799     },
29800     /**
29801      * Sets this button's click handler
29802      * @param {Function} handler The function to call when the button is clicked
29803      * @param {Object} scope (optional) Scope for the function passed above
29804      */
29805     setHandler : function(handler, scope){
29806         this.handler = handler;
29807         this.scope = scope;  
29808     },
29809     
29810     /**
29811      * Sets this button's arrow click handler
29812      * @param {Function} handler The function to call when the arrow is clicked
29813      * @param {Object} scope (optional) Scope for the function passed above
29814      */
29815     setArrowHandler : function(handler, scope){
29816         this.arrowHandler = handler;
29817         this.scope = scope;  
29818     },
29819     
29820     /**
29821      * Focus the button
29822      */
29823     focus : function(){
29824         if(this.el){
29825             this.el.child("button:first").focus();
29826         }
29827     },
29828
29829     // private
29830     onClick : function(e){
29831         e.preventDefault();
29832         if(!this.disabled){
29833             if(e.getTarget(".x-btn-menu-arrow-wrap")){
29834                 if(this.menu && !this.menu.isVisible()){
29835                     this.menu.show(this.el, this.menuAlign);
29836                 }
29837                 this.fireEvent("arrowclick", this, e);
29838                 if(this.arrowHandler){
29839                     this.arrowHandler.call(this.scope || this, this, e);
29840                 }
29841             }else{
29842                 this.fireEvent("click", this, e);
29843                 if(this.handler){
29844                     this.handler.call(this.scope || this, this, e);
29845                 }
29846             }
29847         }
29848     },
29849     // private
29850     onMouseDown : function(e){
29851         if(!this.disabled){
29852             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
29853         }
29854     },
29855     // private
29856     onMouseUp : function(e){
29857         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
29858     }   
29859 });
29860
29861
29862 // backwards compat
29863 Roo.MenuButton = Roo.SplitButton;/*
29864  * Based on:
29865  * Ext JS Library 1.1.1
29866  * Copyright(c) 2006-2007, Ext JS, LLC.
29867  *
29868  * Originally Released Under LGPL - original licence link has changed is not relivant.
29869  *
29870  * Fork - LGPL
29871  * <script type="text/javascript">
29872  */
29873
29874 /**
29875  * @class Roo.Toolbar
29876  * Basic Toolbar class.
29877  * @constructor
29878  * Creates a new Toolbar
29879  * @param {Object} container The config object
29880  */ 
29881 Roo.Toolbar = function(container, buttons, config)
29882 {
29883     /// old consturctor format still supported..
29884     if(container instanceof Array){ // omit the container for later rendering
29885         buttons = container;
29886         config = buttons;
29887         container = null;
29888     }
29889     if (typeof(container) == 'object' && container.xtype) {
29890         config = container;
29891         container = config.container;
29892         buttons = config.buttons || []; // not really - use items!!
29893     }
29894     var xitems = [];
29895     if (config && config.items) {
29896         xitems = config.items;
29897         delete config.items;
29898     }
29899     Roo.apply(this, config);
29900     this.buttons = buttons;
29901     
29902     if(container){
29903         this.render(container);
29904     }
29905     this.xitems = xitems;
29906     Roo.each(xitems, function(b) {
29907         this.add(b);
29908     }, this);
29909     
29910 };
29911
29912 Roo.Toolbar.prototype = {
29913     /**
29914      * @cfg {Array} items
29915      * array of button configs or elements to add (will be converted to a MixedCollection)
29916      */
29917     
29918     /**
29919      * @cfg {String/HTMLElement/Element} container
29920      * The id or element that will contain the toolbar
29921      */
29922     // private
29923     render : function(ct){
29924         this.el = Roo.get(ct);
29925         if(this.cls){
29926             this.el.addClass(this.cls);
29927         }
29928         // using a table allows for vertical alignment
29929         // 100% width is needed by Safari...
29930         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
29931         this.tr = this.el.child("tr", true);
29932         var autoId = 0;
29933         this.items = new Roo.util.MixedCollection(false, function(o){
29934             return o.id || ("item" + (++autoId));
29935         });
29936         if(this.buttons){
29937             this.add.apply(this, this.buttons);
29938             delete this.buttons;
29939         }
29940     },
29941
29942     /**
29943      * Adds element(s) to the toolbar -- this function takes a variable number of 
29944      * arguments of mixed type and adds them to the toolbar.
29945      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
29946      * <ul>
29947      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
29948      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
29949      * <li>Field: Any form field (equivalent to {@link #addField})</li>
29950      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
29951      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
29952      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
29953      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
29954      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
29955      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
29956      * </ul>
29957      * @param {Mixed} arg2
29958      * @param {Mixed} etc.
29959      */
29960     add : function(){
29961         var a = arguments, l = a.length;
29962         for(var i = 0; i < l; i++){
29963             this._add(a[i]);
29964         }
29965     },
29966     // private..
29967     _add : function(el) {
29968         
29969         if (el.xtype) {
29970             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
29971         }
29972         
29973         if (el.applyTo){ // some kind of form field
29974             return this.addField(el);
29975         } 
29976         if (el.render){ // some kind of Toolbar.Item
29977             return this.addItem(el);
29978         }
29979         if (typeof el == "string"){ // string
29980             if(el == "separator" || el == "-"){
29981                 return this.addSeparator();
29982             }
29983             if (el == " "){
29984                 return this.addSpacer();
29985             }
29986             if(el == "->"){
29987                 return this.addFill();
29988             }
29989             return this.addText(el);
29990             
29991         }
29992         if(el.tagName){ // element
29993             return this.addElement(el);
29994         }
29995         if(typeof el == "object"){ // must be button config?
29996             return this.addButton(el);
29997         }
29998         // and now what?!?!
29999         return false;
30000         
30001     },
30002     
30003     /**
30004      * Add an Xtype element
30005      * @param {Object} xtype Xtype Object
30006      * @return {Object} created Object
30007      */
30008     addxtype : function(e){
30009         return this.add(e);  
30010     },
30011     
30012     /**
30013      * Returns the Element for this toolbar.
30014      * @return {Roo.Element}
30015      */
30016     getEl : function(){
30017         return this.el;  
30018     },
30019     
30020     /**
30021      * Adds a separator
30022      * @return {Roo.Toolbar.Item} The separator item
30023      */
30024     addSeparator : function(){
30025         return this.addItem(new Roo.Toolbar.Separator());
30026     },
30027
30028     /**
30029      * Adds a spacer element
30030      * @return {Roo.Toolbar.Spacer} The spacer item
30031      */
30032     addSpacer : function(){
30033         return this.addItem(new Roo.Toolbar.Spacer());
30034     },
30035
30036     /**
30037      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30038      * @return {Roo.Toolbar.Fill} The fill item
30039      */
30040     addFill : function(){
30041         return this.addItem(new Roo.Toolbar.Fill());
30042     },
30043
30044     /**
30045      * Adds any standard HTML element to the toolbar
30046      * @param {String/HTMLElement/Element} el The element or id of the element to add
30047      * @return {Roo.Toolbar.Item} The element's item
30048      */
30049     addElement : function(el){
30050         return this.addItem(new Roo.Toolbar.Item(el));
30051     },
30052     /**
30053      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30054      * @type Roo.util.MixedCollection  
30055      */
30056     items : false,
30057      
30058     /**
30059      * Adds any Toolbar.Item or subclass
30060      * @param {Roo.Toolbar.Item} item
30061      * @return {Roo.Toolbar.Item} The item
30062      */
30063     addItem : function(item){
30064         var td = this.nextBlock();
30065         item.render(td);
30066         this.items.add(item);
30067         return item;
30068     },
30069     
30070     /**
30071      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30072      * @param {Object/Array} config A button config or array of configs
30073      * @return {Roo.Toolbar.Button/Array}
30074      */
30075     addButton : function(config){
30076         if(config instanceof Array){
30077             var buttons = [];
30078             for(var i = 0, len = config.length; i < len; i++) {
30079                 buttons.push(this.addButton(config[i]));
30080             }
30081             return buttons;
30082         }
30083         var b = config;
30084         if(!(config instanceof Roo.Toolbar.Button)){
30085             b = config.split ?
30086                 new Roo.Toolbar.SplitButton(config) :
30087                 new Roo.Toolbar.Button(config);
30088         }
30089         var td = this.nextBlock();
30090         b.render(td);
30091         this.items.add(b);
30092         return b;
30093     },
30094     
30095     /**
30096      * Adds text to the toolbar
30097      * @param {String} text The text to add
30098      * @return {Roo.Toolbar.Item} The element's item
30099      */
30100     addText : function(text){
30101         return this.addItem(new Roo.Toolbar.TextItem(text));
30102     },
30103     
30104     /**
30105      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30106      * @param {Number} index The index where the item is to be inserted
30107      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30108      * @return {Roo.Toolbar.Button/Item}
30109      */
30110     insertButton : function(index, item){
30111         if(item instanceof Array){
30112             var buttons = [];
30113             for(var i = 0, len = item.length; i < len; i++) {
30114                buttons.push(this.insertButton(index + i, item[i]));
30115             }
30116             return buttons;
30117         }
30118         if (!(item instanceof Roo.Toolbar.Button)){
30119            item = new Roo.Toolbar.Button(item);
30120         }
30121         var td = document.createElement("td");
30122         this.tr.insertBefore(td, this.tr.childNodes[index]);
30123         item.render(td);
30124         this.items.insert(index, item);
30125         return item;
30126     },
30127     
30128     /**
30129      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30130      * @param {Object} config
30131      * @return {Roo.Toolbar.Item} The element's item
30132      */
30133     addDom : function(config, returnEl){
30134         var td = this.nextBlock();
30135         Roo.DomHelper.overwrite(td, config);
30136         var ti = new Roo.Toolbar.Item(td.firstChild);
30137         ti.render(td);
30138         this.items.add(ti);
30139         return ti;
30140     },
30141
30142     /**
30143      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30144      * @type Roo.util.MixedCollection  
30145      */
30146     fields : false,
30147     
30148     /**
30149      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30150      * Note: the field should not have been rendered yet. For a field that has already been
30151      * rendered, use {@link #addElement}.
30152      * @param {Roo.form.Field} field
30153      * @return {Roo.ToolbarItem}
30154      */
30155      
30156       
30157     addField : function(field) {
30158         if (!this.fields) {
30159             var autoId = 0;
30160             this.fields = new Roo.util.MixedCollection(false, function(o){
30161                 return o.id || ("item" + (++autoId));
30162             });
30163
30164         }
30165         
30166         var td = this.nextBlock();
30167         field.render(td);
30168         var ti = new Roo.Toolbar.Item(td.firstChild);
30169         ti.render(td);
30170         this.items.add(ti);
30171         this.fields.add(field);
30172         return ti;
30173     },
30174     /**
30175      * Hide the toolbar
30176      * @method hide
30177      */
30178      
30179       
30180     hide : function()
30181     {
30182         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30183         this.el.child('div').hide();
30184     },
30185     /**
30186      * Show the toolbar
30187      * @method show
30188      */
30189     show : function()
30190     {
30191         this.el.child('div').show();
30192     },
30193       
30194     // private
30195     nextBlock : function(){
30196         var td = document.createElement("td");
30197         this.tr.appendChild(td);
30198         return td;
30199     },
30200
30201     // private
30202     destroy : function(){
30203         if(this.items){ // rendered?
30204             Roo.destroy.apply(Roo, this.items.items);
30205         }
30206         if(this.fields){ // rendered?
30207             Roo.destroy.apply(Roo, this.fields.items);
30208         }
30209         Roo.Element.uncache(this.el, this.tr);
30210     }
30211 };
30212
30213 /**
30214  * @class Roo.Toolbar.Item
30215  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30216  * @constructor
30217  * Creates a new Item
30218  * @param {HTMLElement} el 
30219  */
30220 Roo.Toolbar.Item = function(el){
30221     var cfg = {};
30222     if (typeof (el.xtype) != 'undefined') {
30223         cfg = el;
30224         el = cfg.el;
30225     }
30226     
30227     this.el = Roo.getDom(el);
30228     this.id = Roo.id(this.el);
30229     this.hidden = false;
30230     
30231     this.addEvents({
30232          /**
30233              * @event render
30234              * Fires when the button is rendered
30235              * @param {Button} this
30236              */
30237         'render': true
30238     });
30239     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30240 };
30241 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30242 //Roo.Toolbar.Item.prototype = {
30243     
30244     /**
30245      * Get this item's HTML Element
30246      * @return {HTMLElement}
30247      */
30248     getEl : function(){
30249        return this.el;  
30250     },
30251
30252     // private
30253     render : function(td){
30254         
30255          this.td = td;
30256         td.appendChild(this.el);
30257         
30258         this.fireEvent('render', this);
30259     },
30260     
30261     /**
30262      * Removes and destroys this item.
30263      */
30264     destroy : function(){
30265         this.td.parentNode.removeChild(this.td);
30266     },
30267     
30268     /**
30269      * Shows this item.
30270      */
30271     show: function(){
30272         this.hidden = false;
30273         this.td.style.display = "";
30274     },
30275     
30276     /**
30277      * Hides this item.
30278      */
30279     hide: function(){
30280         this.hidden = true;
30281         this.td.style.display = "none";
30282     },
30283     
30284     /**
30285      * Convenience function for boolean show/hide.
30286      * @param {Boolean} visible true to show/false to hide
30287      */
30288     setVisible: function(visible){
30289         if(visible) {
30290             this.show();
30291         }else{
30292             this.hide();
30293         }
30294     },
30295     
30296     /**
30297      * Try to focus this item.
30298      */
30299     focus : function(){
30300         Roo.fly(this.el).focus();
30301     },
30302     
30303     /**
30304      * Disables this item.
30305      */
30306     disable : function(){
30307         Roo.fly(this.td).addClass("x-item-disabled");
30308         this.disabled = true;
30309         this.el.disabled = true;
30310     },
30311     
30312     /**
30313      * Enables this item.
30314      */
30315     enable : function(){
30316         Roo.fly(this.td).removeClass("x-item-disabled");
30317         this.disabled = false;
30318         this.el.disabled = false;
30319     }
30320 });
30321
30322
30323 /**
30324  * @class Roo.Toolbar.Separator
30325  * @extends Roo.Toolbar.Item
30326  * A simple toolbar separator class
30327  * @constructor
30328  * Creates a new Separator
30329  */
30330 Roo.Toolbar.Separator = function(cfg){
30331     
30332     var s = document.createElement("span");
30333     s.className = "ytb-sep";
30334     if (cfg) {
30335         cfg.el = s;
30336     }
30337     
30338     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30339 };
30340 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30341     enable:Roo.emptyFn,
30342     disable:Roo.emptyFn,
30343     focus:Roo.emptyFn
30344 });
30345
30346 /**
30347  * @class Roo.Toolbar.Spacer
30348  * @extends Roo.Toolbar.Item
30349  * A simple element that adds extra horizontal space to a toolbar.
30350  * @constructor
30351  * Creates a new Spacer
30352  */
30353 Roo.Toolbar.Spacer = function(cfg){
30354     var s = document.createElement("div");
30355     s.className = "ytb-spacer";
30356     if (cfg) {
30357         cfg.el = s;
30358     }
30359     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30360 };
30361 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30362     enable:Roo.emptyFn,
30363     disable:Roo.emptyFn,
30364     focus:Roo.emptyFn
30365 });
30366
30367 /**
30368  * @class Roo.Toolbar.Fill
30369  * @extends Roo.Toolbar.Spacer
30370  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30371  * @constructor
30372  * Creates a new Spacer
30373  */
30374 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30375     // private
30376     render : function(td){
30377         td.style.width = '100%';
30378         Roo.Toolbar.Fill.superclass.render.call(this, td);
30379     }
30380 });
30381
30382 /**
30383  * @class Roo.Toolbar.TextItem
30384  * @extends Roo.Toolbar.Item
30385  * A simple class that renders text directly into a toolbar.
30386  * @constructor
30387  * Creates a new TextItem
30388  * @param {String} text
30389  */
30390 Roo.Toolbar.TextItem = function(cfg){
30391     var  text = cfg || "";
30392     if (typeof(cfg) == 'object') {
30393         text = cfg.text || "";
30394     }  else {
30395         cfg = null;
30396     }
30397     var s = document.createElement("span");
30398     s.className = "ytb-text";
30399     s.innerHTML = text;
30400     if (cfg) {
30401         cfg.el  = s;
30402     }
30403     
30404     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30405 };
30406 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30407     
30408      
30409     enable:Roo.emptyFn,
30410     disable:Roo.emptyFn,
30411     focus:Roo.emptyFn
30412 });
30413
30414 /**
30415  * @class Roo.Toolbar.Button
30416  * @extends Roo.Button
30417  * A button that renders into a toolbar.
30418  * @constructor
30419  * Creates a new Button
30420  * @param {Object} config A standard {@link Roo.Button} config object
30421  */
30422 Roo.Toolbar.Button = function(config){
30423     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30424 };
30425 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30426     render : function(td){
30427         this.td = td;
30428         Roo.Toolbar.Button.superclass.render.call(this, td);
30429     },
30430     
30431     /**
30432      * Removes and destroys this button
30433      */
30434     destroy : function(){
30435         Roo.Toolbar.Button.superclass.destroy.call(this);
30436         this.td.parentNode.removeChild(this.td);
30437     },
30438     
30439     /**
30440      * Shows this button
30441      */
30442     show: function(){
30443         this.hidden = false;
30444         this.td.style.display = "";
30445     },
30446     
30447     /**
30448      * Hides this button
30449      */
30450     hide: function(){
30451         this.hidden = true;
30452         this.td.style.display = "none";
30453     },
30454
30455     /**
30456      * Disables this item
30457      */
30458     disable : function(){
30459         Roo.fly(this.td).addClass("x-item-disabled");
30460         this.disabled = true;
30461     },
30462
30463     /**
30464      * Enables this item
30465      */
30466     enable : function(){
30467         Roo.fly(this.td).removeClass("x-item-disabled");
30468         this.disabled = false;
30469     }
30470 });
30471 // backwards compat
30472 Roo.ToolbarButton = Roo.Toolbar.Button;
30473
30474 /**
30475  * @class Roo.Toolbar.SplitButton
30476  * @extends Roo.SplitButton
30477  * A menu button that renders into a toolbar.
30478  * @constructor
30479  * Creates a new SplitButton
30480  * @param {Object} config A standard {@link Roo.SplitButton} config object
30481  */
30482 Roo.Toolbar.SplitButton = function(config){
30483     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30484 };
30485 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30486     render : function(td){
30487         this.td = td;
30488         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30489     },
30490     
30491     /**
30492      * Removes and destroys this button
30493      */
30494     destroy : function(){
30495         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30496         this.td.parentNode.removeChild(this.td);
30497     },
30498     
30499     /**
30500      * Shows this button
30501      */
30502     show: function(){
30503         this.hidden = false;
30504         this.td.style.display = "";
30505     },
30506     
30507     /**
30508      * Hides this button
30509      */
30510     hide: function(){
30511         this.hidden = true;
30512         this.td.style.display = "none";
30513     }
30514 });
30515
30516 // backwards compat
30517 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30518  * Based on:
30519  * Ext JS Library 1.1.1
30520  * Copyright(c) 2006-2007, Ext JS, LLC.
30521  *
30522  * Originally Released Under LGPL - original licence link has changed is not relivant.
30523  *
30524  * Fork - LGPL
30525  * <script type="text/javascript">
30526  */
30527  
30528 /**
30529  * @class Roo.PagingToolbar
30530  * @extends Roo.Toolbar
30531  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30532  * @constructor
30533  * Create a new PagingToolbar
30534  * @param {Object} config The config object
30535  */
30536 Roo.PagingToolbar = function(el, ds, config)
30537 {
30538     // old args format still supported... - xtype is prefered..
30539     if (typeof(el) == 'object' && el.xtype) {
30540         // created from xtype...
30541         config = el;
30542         ds = el.dataSource;
30543         el = config.container;
30544     }
30545     var items = [];
30546     if (config.items) {
30547         items = config.items;
30548         config.items = [];
30549     }
30550     
30551     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30552     this.ds = ds;
30553     this.cursor = 0;
30554     this.renderButtons(this.el);
30555     this.bind(ds);
30556     
30557     // supprot items array.
30558    
30559     Roo.each(items, function(e) {
30560         this.add(Roo.factory(e));
30561     },this);
30562     
30563 };
30564
30565 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30566     /**
30567      * @cfg {Roo.data.Store} dataSource
30568      * The underlying data store providing the paged data
30569      */
30570     /**
30571      * @cfg {String/HTMLElement/Element} container
30572      * container The id or element that will contain the toolbar
30573      */
30574     /**
30575      * @cfg {Boolean} displayInfo
30576      * True to display the displayMsg (defaults to false)
30577      */
30578     /**
30579      * @cfg {Number} pageSize
30580      * The number of records to display per page (defaults to 20)
30581      */
30582     pageSize: 20,
30583     /**
30584      * @cfg {String} displayMsg
30585      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30586      */
30587     displayMsg : 'Displaying {0} - {1} of {2}',
30588     /**
30589      * @cfg {String} emptyMsg
30590      * The message to display when no records are found (defaults to "No data to display")
30591      */
30592     emptyMsg : 'No data to display',
30593     /**
30594      * Customizable piece of the default paging text (defaults to "Page")
30595      * @type String
30596      */
30597     beforePageText : "Page",
30598     /**
30599      * Customizable piece of the default paging text (defaults to "of %0")
30600      * @type String
30601      */
30602     afterPageText : "of {0}",
30603     /**
30604      * Customizable piece of the default paging text (defaults to "First Page")
30605      * @type String
30606      */
30607     firstText : "First Page",
30608     /**
30609      * Customizable piece of the default paging text (defaults to "Previous Page")
30610      * @type String
30611      */
30612     prevText : "Previous Page",
30613     /**
30614      * Customizable piece of the default paging text (defaults to "Next Page")
30615      * @type String
30616      */
30617     nextText : "Next Page",
30618     /**
30619      * Customizable piece of the default paging text (defaults to "Last Page")
30620      * @type String
30621      */
30622     lastText : "Last Page",
30623     /**
30624      * Customizable piece of the default paging text (defaults to "Refresh")
30625      * @type String
30626      */
30627     refreshText : "Refresh",
30628
30629     // private
30630     renderButtons : function(el){
30631         Roo.PagingToolbar.superclass.render.call(this, el);
30632         this.first = this.addButton({
30633             tooltip: this.firstText,
30634             cls: "x-btn-icon x-grid-page-first",
30635             disabled: true,
30636             handler: this.onClick.createDelegate(this, ["first"])
30637         });
30638         this.prev = this.addButton({
30639             tooltip: this.prevText,
30640             cls: "x-btn-icon x-grid-page-prev",
30641             disabled: true,
30642             handler: this.onClick.createDelegate(this, ["prev"])
30643         });
30644         //this.addSeparator();
30645         this.add(this.beforePageText);
30646         this.field = Roo.get(this.addDom({
30647            tag: "input",
30648            type: "text",
30649            size: "3",
30650            value: "1",
30651            cls: "x-grid-page-number"
30652         }).el);
30653         this.field.on("keydown", this.onPagingKeydown, this);
30654         this.field.on("focus", function(){this.dom.select();});
30655         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30656         this.field.setHeight(18);
30657         //this.addSeparator();
30658         this.next = this.addButton({
30659             tooltip: this.nextText,
30660             cls: "x-btn-icon x-grid-page-next",
30661             disabled: true,
30662             handler: this.onClick.createDelegate(this, ["next"])
30663         });
30664         this.last = this.addButton({
30665             tooltip: this.lastText,
30666             cls: "x-btn-icon x-grid-page-last",
30667             disabled: true,
30668             handler: this.onClick.createDelegate(this, ["last"])
30669         });
30670         //this.addSeparator();
30671         this.loading = this.addButton({
30672             tooltip: this.refreshText,
30673             cls: "x-btn-icon x-grid-loading",
30674             handler: this.onClick.createDelegate(this, ["refresh"])
30675         });
30676
30677         if(this.displayInfo){
30678             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30679         }
30680     },
30681
30682     // private
30683     updateInfo : function(){
30684         if(this.displayEl){
30685             var count = this.ds.getCount();
30686             var msg = count == 0 ?
30687                 this.emptyMsg :
30688                 String.format(
30689                     this.displayMsg,
30690                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30691                 );
30692             this.displayEl.update(msg);
30693         }
30694     },
30695
30696     // private
30697     onLoad : function(ds, r, o){
30698        this.cursor = o.params ? o.params.start : 0;
30699        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30700
30701        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30702        this.field.dom.value = ap;
30703        this.first.setDisabled(ap == 1);
30704        this.prev.setDisabled(ap == 1);
30705        this.next.setDisabled(ap == ps);
30706        this.last.setDisabled(ap == ps);
30707        this.loading.enable();
30708        this.updateInfo();
30709     },
30710
30711     // private
30712     getPageData : function(){
30713         var total = this.ds.getTotalCount();
30714         return {
30715             total : total,
30716             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30717             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30718         };
30719     },
30720
30721     // private
30722     onLoadError : function(){
30723         this.loading.enable();
30724     },
30725
30726     // private
30727     onPagingKeydown : function(e){
30728         var k = e.getKey();
30729         var d = this.getPageData();
30730         if(k == e.RETURN){
30731             var v = this.field.dom.value, pageNum;
30732             if(!v || isNaN(pageNum = parseInt(v, 10))){
30733                 this.field.dom.value = d.activePage;
30734                 return;
30735             }
30736             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30737             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30738             e.stopEvent();
30739         }
30740         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))
30741         {
30742           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30743           this.field.dom.value = pageNum;
30744           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30745           e.stopEvent();
30746         }
30747         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30748         {
30749           var v = this.field.dom.value, pageNum; 
30750           var increment = (e.shiftKey) ? 10 : 1;
30751           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30752             increment *= -1;
30753           }
30754           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30755             this.field.dom.value = d.activePage;
30756             return;
30757           }
30758           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30759           {
30760             this.field.dom.value = parseInt(v, 10) + increment;
30761             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30762             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30763           }
30764           e.stopEvent();
30765         }
30766     },
30767
30768     // private
30769     beforeLoad : function(){
30770         if(this.loading){
30771             this.loading.disable();
30772         }
30773     },
30774
30775     // private
30776     onClick : function(which){
30777         var ds = this.ds;
30778         switch(which){
30779             case "first":
30780                 ds.load({params:{start: 0, limit: this.pageSize}});
30781             break;
30782             case "prev":
30783                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
30784             break;
30785             case "next":
30786                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
30787             break;
30788             case "last":
30789                 var total = ds.getTotalCount();
30790                 var extra = total % this.pageSize;
30791                 var lastStart = extra ? (total - extra) : total-this.pageSize;
30792                 ds.load({params:{start: lastStart, limit: this.pageSize}});
30793             break;
30794             case "refresh":
30795                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
30796             break;
30797         }
30798     },
30799
30800     /**
30801      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
30802      * @param {Roo.data.Store} store The data store to unbind
30803      */
30804     unbind : function(ds){
30805         ds.un("beforeload", this.beforeLoad, this);
30806         ds.un("load", this.onLoad, this);
30807         ds.un("loadexception", this.onLoadError, this);
30808         ds.un("remove", this.updateInfo, this);
30809         ds.un("add", this.updateInfo, this);
30810         this.ds = undefined;
30811     },
30812
30813     /**
30814      * Binds the paging toolbar to the specified {@link Roo.data.Store}
30815      * @param {Roo.data.Store} store The data store to bind
30816      */
30817     bind : function(ds){
30818         ds.on("beforeload", this.beforeLoad, this);
30819         ds.on("load", this.onLoad, this);
30820         ds.on("loadexception", this.onLoadError, this);
30821         ds.on("remove", this.updateInfo, this);
30822         ds.on("add", this.updateInfo, this);
30823         this.ds = ds;
30824     }
30825 });/*
30826  * Based on:
30827  * Ext JS Library 1.1.1
30828  * Copyright(c) 2006-2007, Ext JS, LLC.
30829  *
30830  * Originally Released Under LGPL - original licence link has changed is not relivant.
30831  *
30832  * Fork - LGPL
30833  * <script type="text/javascript">
30834  */
30835
30836 /**
30837  * @class Roo.Resizable
30838  * @extends Roo.util.Observable
30839  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
30840  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
30841  * 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
30842  * the element will be wrapped for you automatically.</p>
30843  * <p>Here is the list of valid resize handles:</p>
30844  * <pre>
30845 Value   Description
30846 ------  -------------------
30847  'n'     north
30848  's'     south
30849  'e'     east
30850  'w'     west
30851  'nw'    northwest
30852  'sw'    southwest
30853  'se'    southeast
30854  'ne'    northeast
30855  'hd'    horizontal drag
30856  'all'   all
30857 </pre>
30858  * <p>Here's an example showing the creation of a typical Resizable:</p>
30859  * <pre><code>
30860 var resizer = new Roo.Resizable("element-id", {
30861     handles: 'all',
30862     minWidth: 200,
30863     minHeight: 100,
30864     maxWidth: 500,
30865     maxHeight: 400,
30866     pinned: true
30867 });
30868 resizer.on("resize", myHandler);
30869 </code></pre>
30870  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
30871  * resizer.east.setDisplayed(false);</p>
30872  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
30873  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
30874  * resize operation's new size (defaults to [0, 0])
30875  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
30876  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
30877  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
30878  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
30879  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
30880  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
30881  * @cfg {Number} width The width of the element in pixels (defaults to null)
30882  * @cfg {Number} height The height of the element in pixels (defaults to null)
30883  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
30884  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
30885  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
30886  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
30887  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
30888  * in favor of the handles config option (defaults to false)
30889  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
30890  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
30891  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
30892  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
30893  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
30894  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
30895  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
30896  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
30897  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
30898  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
30899  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
30900  * @constructor
30901  * Create a new resizable component
30902  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
30903  * @param {Object} config configuration options
30904   */
30905 Roo.Resizable = function(el, config)
30906 {
30907     this.el = Roo.get(el);
30908
30909     if(config && config.wrap){
30910         config.resizeChild = this.el;
30911         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
30912         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
30913         this.el.setStyle("overflow", "hidden");
30914         this.el.setPositioning(config.resizeChild.getPositioning());
30915         config.resizeChild.clearPositioning();
30916         if(!config.width || !config.height){
30917             var csize = config.resizeChild.getSize();
30918             this.el.setSize(csize.width, csize.height);
30919         }
30920         if(config.pinned && !config.adjustments){
30921             config.adjustments = "auto";
30922         }
30923     }
30924
30925     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
30926     this.proxy.unselectable();
30927     this.proxy.enableDisplayMode('block');
30928
30929     Roo.apply(this, config);
30930
30931     if(this.pinned){
30932         this.disableTrackOver = true;
30933         this.el.addClass("x-resizable-pinned");
30934     }
30935     // if the element isn't positioned, make it relative
30936     var position = this.el.getStyle("position");
30937     if(position != "absolute" && position != "fixed"){
30938         this.el.setStyle("position", "relative");
30939     }
30940     if(!this.handles){ // no handles passed, must be legacy style
30941         this.handles = 's,e,se';
30942         if(this.multiDirectional){
30943             this.handles += ',n,w';
30944         }
30945     }
30946     if(this.handles == "all"){
30947         this.handles = "n s e w ne nw se sw";
30948     }
30949     var hs = this.handles.split(/\s*?[,;]\s*?| /);
30950     var ps = Roo.Resizable.positions;
30951     for(var i = 0, len = hs.length; i < len; i++){
30952         if(hs[i] && ps[hs[i]]){
30953             var pos = ps[hs[i]];
30954             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
30955         }
30956     }
30957     // legacy
30958     this.corner = this.southeast;
30959     
30960     // updateBox = the box can move..
30961     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
30962         this.updateBox = true;
30963     }
30964
30965     this.activeHandle = null;
30966
30967     if(this.resizeChild){
30968         if(typeof this.resizeChild == "boolean"){
30969             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
30970         }else{
30971             this.resizeChild = Roo.get(this.resizeChild, true);
30972         }
30973     }
30974     
30975     if(this.adjustments == "auto"){
30976         var rc = this.resizeChild;
30977         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
30978         if(rc && (hw || hn)){
30979             rc.position("relative");
30980             rc.setLeft(hw ? hw.el.getWidth() : 0);
30981             rc.setTop(hn ? hn.el.getHeight() : 0);
30982         }
30983         this.adjustments = [
30984             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
30985             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
30986         ];
30987     }
30988
30989     if(this.draggable){
30990         this.dd = this.dynamic ?
30991             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
30992         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
30993     }
30994
30995     // public events
30996     this.addEvents({
30997         /**
30998          * @event beforeresize
30999          * Fired before resize is allowed. Set enabled to false to cancel resize.
31000          * @param {Roo.Resizable} this
31001          * @param {Roo.EventObject} e The mousedown event
31002          */
31003         "beforeresize" : true,
31004         /**
31005          * @event resizing
31006          * Fired a resizing.
31007          * @param {Roo.Resizable} this
31008          * @param {Number} x The new x position
31009          * @param {Number} y The new y position
31010          * @param {Number} w The new w width
31011          * @param {Number} h The new h hight
31012          * @param {Roo.EventObject} e The mouseup event
31013          */
31014         "resizing" : true,
31015         /**
31016          * @event resize
31017          * Fired after a resize.
31018          * @param {Roo.Resizable} this
31019          * @param {Number} width The new width
31020          * @param {Number} height The new height
31021          * @param {Roo.EventObject} e The mouseup event
31022          */
31023         "resize" : true
31024     });
31025
31026     if(this.width !== null && this.height !== null){
31027         this.resizeTo(this.width, this.height);
31028     }else{
31029         this.updateChildSize();
31030     }
31031     if(Roo.isIE){
31032         this.el.dom.style.zoom = 1;
31033     }
31034     Roo.Resizable.superclass.constructor.call(this);
31035 };
31036
31037 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31038         resizeChild : false,
31039         adjustments : [0, 0],
31040         minWidth : 5,
31041         minHeight : 5,
31042         maxWidth : 10000,
31043         maxHeight : 10000,
31044         enabled : true,
31045         animate : false,
31046         duration : .35,
31047         dynamic : false,
31048         handles : false,
31049         multiDirectional : false,
31050         disableTrackOver : false,
31051         easing : 'easeOutStrong',
31052         widthIncrement : 0,
31053         heightIncrement : 0,
31054         pinned : false,
31055         width : null,
31056         height : null,
31057         preserveRatio : false,
31058         transparent: false,
31059         minX: 0,
31060         minY: 0,
31061         draggable: false,
31062
31063         /**
31064          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31065          */
31066         constrainTo: undefined,
31067         /**
31068          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31069          */
31070         resizeRegion: undefined,
31071
31072
31073     /**
31074      * Perform a manual resize
31075      * @param {Number} width
31076      * @param {Number} height
31077      */
31078     resizeTo : function(width, height){
31079         this.el.setSize(width, height);
31080         this.updateChildSize();
31081         this.fireEvent("resize", this, width, height, null);
31082     },
31083
31084     // private
31085     startSizing : function(e, handle){
31086         this.fireEvent("beforeresize", this, e);
31087         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31088
31089             if(!this.overlay){
31090                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31091                 this.overlay.unselectable();
31092                 this.overlay.enableDisplayMode("block");
31093                 this.overlay.on("mousemove", this.onMouseMove, this);
31094                 this.overlay.on("mouseup", this.onMouseUp, this);
31095             }
31096             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31097
31098             this.resizing = true;
31099             this.startBox = this.el.getBox();
31100             this.startPoint = e.getXY();
31101             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31102                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31103
31104             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31105             this.overlay.show();
31106
31107             if(this.constrainTo) {
31108                 var ct = Roo.get(this.constrainTo);
31109                 this.resizeRegion = ct.getRegion().adjust(
31110                     ct.getFrameWidth('t'),
31111                     ct.getFrameWidth('l'),
31112                     -ct.getFrameWidth('b'),
31113                     -ct.getFrameWidth('r')
31114                 );
31115             }
31116
31117             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31118             this.proxy.show();
31119             this.proxy.setBox(this.startBox);
31120             if(!this.dynamic){
31121                 this.proxy.setStyle('visibility', 'visible');
31122             }
31123         }
31124     },
31125
31126     // private
31127     onMouseDown : function(handle, e){
31128         if(this.enabled){
31129             e.stopEvent();
31130             this.activeHandle = handle;
31131             this.startSizing(e, handle);
31132         }
31133     },
31134
31135     // private
31136     onMouseUp : function(e){
31137         var size = this.resizeElement();
31138         this.resizing = false;
31139         this.handleOut();
31140         this.overlay.hide();
31141         this.proxy.hide();
31142         this.fireEvent("resize", this, size.width, size.height, e);
31143     },
31144
31145     // private
31146     updateChildSize : function(){
31147         
31148         if(this.resizeChild){
31149             var el = this.el;
31150             var child = this.resizeChild;
31151             var adj = this.adjustments;
31152             if(el.dom.offsetWidth){
31153                 var b = el.getSize(true);
31154                 child.setSize(b.width+adj[0], b.height+adj[1]);
31155             }
31156             // Second call here for IE
31157             // The first call enables instant resizing and
31158             // the second call corrects scroll bars if they
31159             // exist
31160             if(Roo.isIE){
31161                 setTimeout(function(){
31162                     if(el.dom.offsetWidth){
31163                         var b = el.getSize(true);
31164                         child.setSize(b.width+adj[0], b.height+adj[1]);
31165                     }
31166                 }, 10);
31167             }
31168         }
31169     },
31170
31171     // private
31172     snap : function(value, inc, min){
31173         if(!inc || !value) {
31174             return value;
31175         }
31176         var newValue = value;
31177         var m = value % inc;
31178         if(m > 0){
31179             if(m > (inc/2)){
31180                 newValue = value + (inc-m);
31181             }else{
31182                 newValue = value - m;
31183             }
31184         }
31185         return Math.max(min, newValue);
31186     },
31187
31188     // private
31189     resizeElement : function(){
31190         var box = this.proxy.getBox();
31191         if(this.updateBox){
31192             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31193         }else{
31194             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31195         }
31196         this.updateChildSize();
31197         if(!this.dynamic){
31198             this.proxy.hide();
31199         }
31200         return box;
31201     },
31202
31203     // private
31204     constrain : function(v, diff, m, mx){
31205         if(v - diff < m){
31206             diff = v - m;
31207         }else if(v - diff > mx){
31208             diff = mx - v;
31209         }
31210         return diff;
31211     },
31212
31213     // private
31214     onMouseMove : function(e){
31215         
31216         if(this.enabled){
31217             try{// try catch so if something goes wrong the user doesn't get hung
31218
31219             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31220                 return;
31221             }
31222
31223             //var curXY = this.startPoint;
31224             var curSize = this.curSize || this.startBox;
31225             var x = this.startBox.x, y = this.startBox.y;
31226             var ox = x, oy = y;
31227             var w = curSize.width, h = curSize.height;
31228             var ow = w, oh = h;
31229             var mw = this.minWidth, mh = this.minHeight;
31230             var mxw = this.maxWidth, mxh = this.maxHeight;
31231             var wi = this.widthIncrement;
31232             var hi = this.heightIncrement;
31233
31234             var eventXY = e.getXY();
31235             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31236             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31237
31238             var pos = this.activeHandle.position;
31239
31240             switch(pos){
31241                 case "east":
31242                     w += diffX;
31243                     w = Math.min(Math.max(mw, w), mxw);
31244                     break;
31245              
31246                 case "south":
31247                     h += diffY;
31248                     h = Math.min(Math.max(mh, h), mxh);
31249                     break;
31250                 case "southeast":
31251                     w += diffX;
31252                     h += diffY;
31253                     w = Math.min(Math.max(mw, w), mxw);
31254                     h = Math.min(Math.max(mh, h), mxh);
31255                     break;
31256                 case "north":
31257                     diffY = this.constrain(h, diffY, mh, mxh);
31258                     y += diffY;
31259                     h -= diffY;
31260                     break;
31261                 case "hdrag":
31262                     
31263                     if (wi) {
31264                         var adiffX = Math.abs(diffX);
31265                         var sub = (adiffX % wi); // how much 
31266                         if (sub > (wi/2)) { // far enough to snap
31267                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31268                         } else {
31269                             // remove difference.. 
31270                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31271                         }
31272                     }
31273                     x += diffX;
31274                     x = Math.max(this.minX, x);
31275                     break;
31276                 case "west":
31277                     diffX = this.constrain(w, diffX, mw, mxw);
31278                     x += diffX;
31279                     w -= diffX;
31280                     break;
31281                 case "northeast":
31282                     w += diffX;
31283                     w = Math.min(Math.max(mw, w), mxw);
31284                     diffY = this.constrain(h, diffY, mh, mxh);
31285                     y += diffY;
31286                     h -= diffY;
31287                     break;
31288                 case "northwest":
31289                     diffX = this.constrain(w, diffX, mw, mxw);
31290                     diffY = this.constrain(h, diffY, mh, mxh);
31291                     y += diffY;
31292                     h -= diffY;
31293                     x += diffX;
31294                     w -= diffX;
31295                     break;
31296                case "southwest":
31297                     diffX = this.constrain(w, diffX, mw, mxw);
31298                     h += diffY;
31299                     h = Math.min(Math.max(mh, h), mxh);
31300                     x += diffX;
31301                     w -= diffX;
31302                     break;
31303             }
31304
31305             var sw = this.snap(w, wi, mw);
31306             var sh = this.snap(h, hi, mh);
31307             if(sw != w || sh != h){
31308                 switch(pos){
31309                     case "northeast":
31310                         y -= sh - h;
31311                     break;
31312                     case "north":
31313                         y -= sh - h;
31314                         break;
31315                     case "southwest":
31316                         x -= sw - w;
31317                     break;
31318                     case "west":
31319                         x -= sw - w;
31320                         break;
31321                     case "northwest":
31322                         x -= sw - w;
31323                         y -= sh - h;
31324                     break;
31325                 }
31326                 w = sw;
31327                 h = sh;
31328             }
31329
31330             if(this.preserveRatio){
31331                 switch(pos){
31332                     case "southeast":
31333                     case "east":
31334                         h = oh * (w/ow);
31335                         h = Math.min(Math.max(mh, h), mxh);
31336                         w = ow * (h/oh);
31337                        break;
31338                     case "south":
31339                         w = ow * (h/oh);
31340                         w = Math.min(Math.max(mw, w), mxw);
31341                         h = oh * (w/ow);
31342                         break;
31343                     case "northeast":
31344                         w = ow * (h/oh);
31345                         w = Math.min(Math.max(mw, w), mxw);
31346                         h = oh * (w/ow);
31347                     break;
31348                     case "north":
31349                         var tw = w;
31350                         w = ow * (h/oh);
31351                         w = Math.min(Math.max(mw, w), mxw);
31352                         h = oh * (w/ow);
31353                         x += (tw - w) / 2;
31354                         break;
31355                     case "southwest":
31356                         h = oh * (w/ow);
31357                         h = Math.min(Math.max(mh, h), mxh);
31358                         var tw = w;
31359                         w = ow * (h/oh);
31360                         x += tw - w;
31361                         break;
31362                     case "west":
31363                         var th = h;
31364                         h = oh * (w/ow);
31365                         h = Math.min(Math.max(mh, h), mxh);
31366                         y += (th - h) / 2;
31367                         var tw = w;
31368                         w = ow * (h/oh);
31369                         x += tw - w;
31370                        break;
31371                     case "northwest":
31372                         var tw = w;
31373                         var th = h;
31374                         h = oh * (w/ow);
31375                         h = Math.min(Math.max(mh, h), mxh);
31376                         w = ow * (h/oh);
31377                         y += th - h;
31378                         x += tw - w;
31379                        break;
31380
31381                 }
31382             }
31383             if (pos == 'hdrag') {
31384                 w = ow;
31385             }
31386             this.proxy.setBounds(x, y, w, h);
31387             if(this.dynamic){
31388                 this.resizeElement();
31389             }
31390             }catch(e){}
31391         }
31392         this.fireEvent("resizing", this, x, y, w, h, e);
31393     },
31394
31395     // private
31396     handleOver : function(){
31397         if(this.enabled){
31398             this.el.addClass("x-resizable-over");
31399         }
31400     },
31401
31402     // private
31403     handleOut : function(){
31404         if(!this.resizing){
31405             this.el.removeClass("x-resizable-over");
31406         }
31407     },
31408
31409     /**
31410      * Returns the element this component is bound to.
31411      * @return {Roo.Element}
31412      */
31413     getEl : function(){
31414         return this.el;
31415     },
31416
31417     /**
31418      * Returns the resizeChild element (or null).
31419      * @return {Roo.Element}
31420      */
31421     getResizeChild : function(){
31422         return this.resizeChild;
31423     },
31424     groupHandler : function()
31425     {
31426         
31427     },
31428     /**
31429      * Destroys this resizable. If the element was wrapped and
31430      * removeEl is not true then the element remains.
31431      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31432      */
31433     destroy : function(removeEl){
31434         this.proxy.remove();
31435         if(this.overlay){
31436             this.overlay.removeAllListeners();
31437             this.overlay.remove();
31438         }
31439         var ps = Roo.Resizable.positions;
31440         for(var k in ps){
31441             if(typeof ps[k] != "function" && this[ps[k]]){
31442                 var h = this[ps[k]];
31443                 h.el.removeAllListeners();
31444                 h.el.remove();
31445             }
31446         }
31447         if(removeEl){
31448             this.el.update("");
31449             this.el.remove();
31450         }
31451     }
31452 });
31453
31454 // private
31455 // hash to map config positions to true positions
31456 Roo.Resizable.positions = {
31457     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31458     hd: "hdrag"
31459 };
31460
31461 // private
31462 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31463     if(!this.tpl){
31464         // only initialize the template if resizable is used
31465         var tpl = Roo.DomHelper.createTemplate(
31466             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31467         );
31468         tpl.compile();
31469         Roo.Resizable.Handle.prototype.tpl = tpl;
31470     }
31471     this.position = pos;
31472     this.rz = rz;
31473     // show north drag fro topdra
31474     var handlepos = pos == 'hdrag' ? 'north' : pos;
31475     
31476     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31477     if (pos == 'hdrag') {
31478         this.el.setStyle('cursor', 'pointer');
31479     }
31480     this.el.unselectable();
31481     if(transparent){
31482         this.el.setOpacity(0);
31483     }
31484     this.el.on("mousedown", this.onMouseDown, this);
31485     if(!disableTrackOver){
31486         this.el.on("mouseover", this.onMouseOver, this);
31487         this.el.on("mouseout", this.onMouseOut, this);
31488     }
31489 };
31490
31491 // private
31492 Roo.Resizable.Handle.prototype = {
31493     afterResize : function(rz){
31494         Roo.log('after?');
31495         // do nothing
31496     },
31497     // private
31498     onMouseDown : function(e){
31499         this.rz.onMouseDown(this, e);
31500     },
31501     // private
31502     onMouseOver : function(e){
31503         this.rz.handleOver(this, e);
31504     },
31505     // private
31506     onMouseOut : function(e){
31507         this.rz.handleOut(this, e);
31508     }
31509 };/*
31510  * Based on:
31511  * Ext JS Library 1.1.1
31512  * Copyright(c) 2006-2007, Ext JS, LLC.
31513  *
31514  * Originally Released Under LGPL - original licence link has changed is not relivant.
31515  *
31516  * Fork - LGPL
31517  * <script type="text/javascript">
31518  */
31519
31520 /**
31521  * @class Roo.Editor
31522  * @extends Roo.Component
31523  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31524  * @constructor
31525  * Create a new Editor
31526  * @param {Roo.form.Field} field The Field object (or descendant)
31527  * @param {Object} config The config object
31528  */
31529 Roo.Editor = function(field, config){
31530     Roo.Editor.superclass.constructor.call(this, config);
31531     this.field = field;
31532     this.addEvents({
31533         /**
31534              * @event beforestartedit
31535              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31536              * false from the handler of this event.
31537              * @param {Editor} this
31538              * @param {Roo.Element} boundEl The underlying element bound to this editor
31539              * @param {Mixed} value The field value being set
31540              */
31541         "beforestartedit" : true,
31542         /**
31543              * @event startedit
31544              * Fires when this editor is displayed
31545              * @param {Roo.Element} boundEl The underlying element bound to this editor
31546              * @param {Mixed} value The starting field value
31547              */
31548         "startedit" : true,
31549         /**
31550              * @event beforecomplete
31551              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31552              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31553              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31554              * event will not fire since no edit actually occurred.
31555              * @param {Editor} this
31556              * @param {Mixed} value The current field value
31557              * @param {Mixed} startValue The original field value
31558              */
31559         "beforecomplete" : true,
31560         /**
31561              * @event complete
31562              * Fires after editing is complete and any changed value has been written to the underlying field.
31563              * @param {Editor} this
31564              * @param {Mixed} value The current field value
31565              * @param {Mixed} startValue The original field value
31566              */
31567         "complete" : true,
31568         /**
31569          * @event specialkey
31570          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31571          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31572          * @param {Roo.form.Field} this
31573          * @param {Roo.EventObject} e The event object
31574          */
31575         "specialkey" : true
31576     });
31577 };
31578
31579 Roo.extend(Roo.Editor, Roo.Component, {
31580     /**
31581      * @cfg {Boolean/String} autosize
31582      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31583      * or "height" to adopt the height only (defaults to false)
31584      */
31585     /**
31586      * @cfg {Boolean} revertInvalid
31587      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31588      * validation fails (defaults to true)
31589      */
31590     /**
31591      * @cfg {Boolean} ignoreNoChange
31592      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31593      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31594      * will never be ignored.
31595      */
31596     /**
31597      * @cfg {Boolean} hideEl
31598      * False to keep the bound element visible while the editor is displayed (defaults to true)
31599      */
31600     /**
31601      * @cfg {Mixed} value
31602      * The data value of the underlying field (defaults to "")
31603      */
31604     value : "",
31605     /**
31606      * @cfg {String} alignment
31607      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31608      */
31609     alignment: "c-c?",
31610     /**
31611      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31612      * for bottom-right shadow (defaults to "frame")
31613      */
31614     shadow : "frame",
31615     /**
31616      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31617      */
31618     constrain : false,
31619     /**
31620      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31621      */
31622     completeOnEnter : false,
31623     /**
31624      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31625      */
31626     cancelOnEsc : false,
31627     /**
31628      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31629      */
31630     updateEl : false,
31631
31632     // private
31633     onRender : function(ct, position){
31634         this.el = new Roo.Layer({
31635             shadow: this.shadow,
31636             cls: "x-editor",
31637             parentEl : ct,
31638             shim : this.shim,
31639             shadowOffset:4,
31640             id: this.id,
31641             constrain: this.constrain
31642         });
31643         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31644         if(this.field.msgTarget != 'title'){
31645             this.field.msgTarget = 'qtip';
31646         }
31647         this.field.render(this.el);
31648         if(Roo.isGecko){
31649             this.field.el.dom.setAttribute('autocomplete', 'off');
31650         }
31651         this.field.on("specialkey", this.onSpecialKey, this);
31652         if(this.swallowKeys){
31653             this.field.el.swallowEvent(['keydown','keypress']);
31654         }
31655         this.field.show();
31656         this.field.on("blur", this.onBlur, this);
31657         if(this.field.grow){
31658             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31659         }
31660     },
31661
31662     onSpecialKey : function(field, e)
31663     {
31664         //Roo.log('editor onSpecialKey');
31665         if(this.completeOnEnter && e.getKey() == e.ENTER){
31666             e.stopEvent();
31667             this.completeEdit();
31668             return;
31669         }
31670         // do not fire special key otherwise it might hide close the editor...
31671         if(e.getKey() == e.ENTER){    
31672             return;
31673         }
31674         if(this.cancelOnEsc && e.getKey() == e.ESC){
31675             this.cancelEdit();
31676             return;
31677         } 
31678         this.fireEvent('specialkey', field, e);
31679     
31680     },
31681
31682     /**
31683      * Starts the editing process and shows the editor.
31684      * @param {String/HTMLElement/Element} el The element to edit
31685      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31686       * to the innerHTML of el.
31687      */
31688     startEdit : function(el, value){
31689         if(this.editing){
31690             this.completeEdit();
31691         }
31692         this.boundEl = Roo.get(el);
31693         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31694         if(!this.rendered){
31695             this.render(this.parentEl || document.body);
31696         }
31697         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31698             return;
31699         }
31700         this.startValue = v;
31701         this.field.setValue(v);
31702         if(this.autoSize){
31703             var sz = this.boundEl.getSize();
31704             switch(this.autoSize){
31705                 case "width":
31706                 this.setSize(sz.width,  "");
31707                 break;
31708                 case "height":
31709                 this.setSize("",  sz.height);
31710                 break;
31711                 default:
31712                 this.setSize(sz.width,  sz.height);
31713             }
31714         }
31715         this.el.alignTo(this.boundEl, this.alignment);
31716         this.editing = true;
31717         if(Roo.QuickTips){
31718             Roo.QuickTips.disable();
31719         }
31720         this.show();
31721     },
31722
31723     /**
31724      * Sets the height and width of this editor.
31725      * @param {Number} width The new width
31726      * @param {Number} height The new height
31727      */
31728     setSize : function(w, h){
31729         this.field.setSize(w, h);
31730         if(this.el){
31731             this.el.sync();
31732         }
31733     },
31734
31735     /**
31736      * Realigns the editor to the bound field based on the current alignment config value.
31737      */
31738     realign : function(){
31739         this.el.alignTo(this.boundEl, this.alignment);
31740     },
31741
31742     /**
31743      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31744      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31745      */
31746     completeEdit : function(remainVisible){
31747         if(!this.editing){
31748             return;
31749         }
31750         var v = this.getValue();
31751         if(this.revertInvalid !== false && !this.field.isValid()){
31752             v = this.startValue;
31753             this.cancelEdit(true);
31754         }
31755         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31756             this.editing = false;
31757             this.hide();
31758             return;
31759         }
31760         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31761             this.editing = false;
31762             if(this.updateEl && this.boundEl){
31763                 this.boundEl.update(v);
31764             }
31765             if(remainVisible !== true){
31766                 this.hide();
31767             }
31768             this.fireEvent("complete", this, v, this.startValue);
31769         }
31770     },
31771
31772     // private
31773     onShow : function(){
31774         this.el.show();
31775         if(this.hideEl !== false){
31776             this.boundEl.hide();
31777         }
31778         this.field.show();
31779         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31780             this.fixIEFocus = true;
31781             this.deferredFocus.defer(50, this);
31782         }else{
31783             this.field.focus();
31784         }
31785         this.fireEvent("startedit", this.boundEl, this.startValue);
31786     },
31787
31788     deferredFocus : function(){
31789         if(this.editing){
31790             this.field.focus();
31791         }
31792     },
31793
31794     /**
31795      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
31796      * reverted to the original starting value.
31797      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
31798      * cancel (defaults to false)
31799      */
31800     cancelEdit : function(remainVisible){
31801         if(this.editing){
31802             this.setValue(this.startValue);
31803             if(remainVisible !== true){
31804                 this.hide();
31805             }
31806         }
31807     },
31808
31809     // private
31810     onBlur : function(){
31811         if(this.allowBlur !== true && this.editing){
31812             this.completeEdit();
31813         }
31814     },
31815
31816     // private
31817     onHide : function(){
31818         if(this.editing){
31819             this.completeEdit();
31820             return;
31821         }
31822         this.field.blur();
31823         if(this.field.collapse){
31824             this.field.collapse();
31825         }
31826         this.el.hide();
31827         if(this.hideEl !== false){
31828             this.boundEl.show();
31829         }
31830         if(Roo.QuickTips){
31831             Roo.QuickTips.enable();
31832         }
31833     },
31834
31835     /**
31836      * Sets the data value of the editor
31837      * @param {Mixed} value Any valid value supported by the underlying field
31838      */
31839     setValue : function(v){
31840         this.field.setValue(v);
31841     },
31842
31843     /**
31844      * Gets the data value of the editor
31845      * @return {Mixed} The data value
31846      */
31847     getValue : function(){
31848         return this.field.getValue();
31849     }
31850 });/*
31851  * Based on:
31852  * Ext JS Library 1.1.1
31853  * Copyright(c) 2006-2007, Ext JS, LLC.
31854  *
31855  * Originally Released Under LGPL - original licence link has changed is not relivant.
31856  *
31857  * Fork - LGPL
31858  * <script type="text/javascript">
31859  */
31860  
31861 /**
31862  * @class Roo.BasicDialog
31863  * @extends Roo.util.Observable
31864  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
31865  * <pre><code>
31866 var dlg = new Roo.BasicDialog("my-dlg", {
31867     height: 200,
31868     width: 300,
31869     minHeight: 100,
31870     minWidth: 150,
31871     modal: true,
31872     proxyDrag: true,
31873     shadow: true
31874 });
31875 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
31876 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
31877 dlg.addButton('Cancel', dlg.hide, dlg);
31878 dlg.show();
31879 </code></pre>
31880   <b>A Dialog should always be a direct child of the body element.</b>
31881  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
31882  * @cfg {String} title Default text to display in the title bar (defaults to null)
31883  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31884  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
31885  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
31886  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
31887  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
31888  * (defaults to null with no animation)
31889  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
31890  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
31891  * property for valid values (defaults to 'all')
31892  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
31893  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
31894  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
31895  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
31896  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
31897  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
31898  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
31899  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
31900  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
31901  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
31902  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
31903  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
31904  * draggable = true (defaults to false)
31905  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
31906  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
31907  * shadow (defaults to false)
31908  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
31909  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
31910  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
31911  * @cfg {Array} buttons Array of buttons
31912  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
31913  * @constructor
31914  * Create a new BasicDialog.
31915  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
31916  * @param {Object} config Configuration options
31917  */
31918 Roo.BasicDialog = function(el, config){
31919     this.el = Roo.get(el);
31920     var dh = Roo.DomHelper;
31921     if(!this.el && config && config.autoCreate){
31922         if(typeof config.autoCreate == "object"){
31923             if(!config.autoCreate.id){
31924                 config.autoCreate.id = el;
31925             }
31926             this.el = dh.append(document.body,
31927                         config.autoCreate, true);
31928         }else{
31929             this.el = dh.append(document.body,
31930                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
31931         }
31932     }
31933     el = this.el;
31934     el.setDisplayed(true);
31935     el.hide = this.hideAction;
31936     this.id = el.id;
31937     el.addClass("x-dlg");
31938
31939     Roo.apply(this, config);
31940
31941     this.proxy = el.createProxy("x-dlg-proxy");
31942     this.proxy.hide = this.hideAction;
31943     this.proxy.setOpacity(.5);
31944     this.proxy.hide();
31945
31946     if(config.width){
31947         el.setWidth(config.width);
31948     }
31949     if(config.height){
31950         el.setHeight(config.height);
31951     }
31952     this.size = el.getSize();
31953     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
31954         this.xy = [config.x,config.y];
31955     }else{
31956         this.xy = el.getCenterXY(true);
31957     }
31958     /** The header element @type Roo.Element */
31959     this.header = el.child("> .x-dlg-hd");
31960     /** The body element @type Roo.Element */
31961     this.body = el.child("> .x-dlg-bd");
31962     /** The footer element @type Roo.Element */
31963     this.footer = el.child("> .x-dlg-ft");
31964
31965     if(!this.header){
31966         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
31967     }
31968     if(!this.body){
31969         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
31970     }
31971
31972     this.header.unselectable();
31973     if(this.title){
31974         this.header.update(this.title);
31975     }
31976     // this element allows the dialog to be focused for keyboard event
31977     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
31978     this.focusEl.swallowEvent("click", true);
31979
31980     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
31981
31982     // wrap the body and footer for special rendering
31983     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
31984     if(this.footer){
31985         this.bwrap.dom.appendChild(this.footer.dom);
31986     }
31987
31988     this.bg = this.el.createChild({
31989         tag: "div", cls:"x-dlg-bg",
31990         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
31991     });
31992     this.centerBg = this.bg.child("div.x-dlg-bg-center");
31993
31994
31995     if(this.autoScroll !== false && !this.autoTabs){
31996         this.body.setStyle("overflow", "auto");
31997     }
31998
31999     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32000
32001     if(this.closable !== false){
32002         this.el.addClass("x-dlg-closable");
32003         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32004         this.close.on("click", this.closeClick, this);
32005         this.close.addClassOnOver("x-dlg-close-over");
32006     }
32007     if(this.collapsible !== false){
32008         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32009         this.collapseBtn.on("click", this.collapseClick, this);
32010         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32011         this.header.on("dblclick", this.collapseClick, this);
32012     }
32013     if(this.resizable !== false){
32014         this.el.addClass("x-dlg-resizable");
32015         this.resizer = new Roo.Resizable(el, {
32016             minWidth: this.minWidth || 80,
32017             minHeight:this.minHeight || 80,
32018             handles: this.resizeHandles || "all",
32019             pinned: true
32020         });
32021         this.resizer.on("beforeresize", this.beforeResize, this);
32022         this.resizer.on("resize", this.onResize, this);
32023     }
32024     if(this.draggable !== false){
32025         el.addClass("x-dlg-draggable");
32026         if (!this.proxyDrag) {
32027             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32028         }
32029         else {
32030             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32031         }
32032         dd.setHandleElId(this.header.id);
32033         dd.endDrag = this.endMove.createDelegate(this);
32034         dd.startDrag = this.startMove.createDelegate(this);
32035         dd.onDrag = this.onDrag.createDelegate(this);
32036         dd.scroll = false;
32037         this.dd = dd;
32038     }
32039     if(this.modal){
32040         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32041         this.mask.enableDisplayMode("block");
32042         this.mask.hide();
32043         this.el.addClass("x-dlg-modal");
32044     }
32045     if(this.shadow){
32046         this.shadow = new Roo.Shadow({
32047             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32048             offset : this.shadowOffset
32049         });
32050     }else{
32051         this.shadowOffset = 0;
32052     }
32053     if(Roo.useShims && this.shim !== false){
32054         this.shim = this.el.createShim();
32055         this.shim.hide = this.hideAction;
32056         this.shim.hide();
32057     }else{
32058         this.shim = false;
32059     }
32060     if(this.autoTabs){
32061         this.initTabs();
32062     }
32063     if (this.buttons) { 
32064         var bts= this.buttons;
32065         this.buttons = [];
32066         Roo.each(bts, function(b) {
32067             this.addButton(b);
32068         }, this);
32069     }
32070     
32071     
32072     this.addEvents({
32073         /**
32074          * @event keydown
32075          * Fires when a key is pressed
32076          * @param {Roo.BasicDialog} this
32077          * @param {Roo.EventObject} e
32078          */
32079         "keydown" : true,
32080         /**
32081          * @event move
32082          * Fires when this dialog is moved by the user.
32083          * @param {Roo.BasicDialog} this
32084          * @param {Number} x The new page X
32085          * @param {Number} y The new page Y
32086          */
32087         "move" : true,
32088         /**
32089          * @event resize
32090          * Fires when this dialog is resized by the user.
32091          * @param {Roo.BasicDialog} this
32092          * @param {Number} width The new width
32093          * @param {Number} height The new height
32094          */
32095         "resize" : true,
32096         /**
32097          * @event beforehide
32098          * Fires before this dialog is hidden.
32099          * @param {Roo.BasicDialog} this
32100          */
32101         "beforehide" : true,
32102         /**
32103          * @event hide
32104          * Fires when this dialog is hidden.
32105          * @param {Roo.BasicDialog} this
32106          */
32107         "hide" : true,
32108         /**
32109          * @event beforeshow
32110          * Fires before this dialog is shown.
32111          * @param {Roo.BasicDialog} this
32112          */
32113         "beforeshow" : true,
32114         /**
32115          * @event show
32116          * Fires when this dialog is shown.
32117          * @param {Roo.BasicDialog} this
32118          */
32119         "show" : true
32120     });
32121     el.on("keydown", this.onKeyDown, this);
32122     el.on("mousedown", this.toFront, this);
32123     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32124     this.el.hide();
32125     Roo.DialogManager.register(this);
32126     Roo.BasicDialog.superclass.constructor.call(this);
32127 };
32128
32129 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32130     shadowOffset: Roo.isIE ? 6 : 5,
32131     minHeight: 80,
32132     minWidth: 200,
32133     minButtonWidth: 75,
32134     defaultButton: null,
32135     buttonAlign: "right",
32136     tabTag: 'div',
32137     firstShow: true,
32138
32139     /**
32140      * Sets the dialog title text
32141      * @param {String} text The title text to display
32142      * @return {Roo.BasicDialog} this
32143      */
32144     setTitle : function(text){
32145         this.header.update(text);
32146         return this;
32147     },
32148
32149     // private
32150     closeClick : function(){
32151         this.hide();
32152     },
32153
32154     // private
32155     collapseClick : function(){
32156         this[this.collapsed ? "expand" : "collapse"]();
32157     },
32158
32159     /**
32160      * Collapses the dialog to its minimized state (only the title bar is visible).
32161      * Equivalent to the user clicking the collapse dialog button.
32162      */
32163     collapse : function(){
32164         if(!this.collapsed){
32165             this.collapsed = true;
32166             this.el.addClass("x-dlg-collapsed");
32167             this.restoreHeight = this.el.getHeight();
32168             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32169         }
32170     },
32171
32172     /**
32173      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32174      * clicking the expand dialog button.
32175      */
32176     expand : function(){
32177         if(this.collapsed){
32178             this.collapsed = false;
32179             this.el.removeClass("x-dlg-collapsed");
32180             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32181         }
32182     },
32183
32184     /**
32185      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32186      * @return {Roo.TabPanel} The tabs component
32187      */
32188     initTabs : function(){
32189         var tabs = this.getTabs();
32190         while(tabs.getTab(0)){
32191             tabs.removeTab(0);
32192         }
32193         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32194             var dom = el.dom;
32195             tabs.addTab(Roo.id(dom), dom.title);
32196             dom.title = "";
32197         });
32198         tabs.activate(0);
32199         return tabs;
32200     },
32201
32202     // private
32203     beforeResize : function(){
32204         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32205     },
32206
32207     // private
32208     onResize : function(){
32209         this.refreshSize();
32210         this.syncBodyHeight();
32211         this.adjustAssets();
32212         this.focus();
32213         this.fireEvent("resize", this, this.size.width, this.size.height);
32214     },
32215
32216     // private
32217     onKeyDown : function(e){
32218         if(this.isVisible()){
32219             this.fireEvent("keydown", this, e);
32220         }
32221     },
32222
32223     /**
32224      * Resizes the dialog.
32225      * @param {Number} width
32226      * @param {Number} height
32227      * @return {Roo.BasicDialog} this
32228      */
32229     resizeTo : function(width, height){
32230         this.el.setSize(width, height);
32231         this.size = {width: width, height: height};
32232         this.syncBodyHeight();
32233         if(this.fixedcenter){
32234             this.center();
32235         }
32236         if(this.isVisible()){
32237             this.constrainXY();
32238             this.adjustAssets();
32239         }
32240         this.fireEvent("resize", this, width, height);
32241         return this;
32242     },
32243
32244
32245     /**
32246      * Resizes the dialog to fit the specified content size.
32247      * @param {Number} width
32248      * @param {Number} height
32249      * @return {Roo.BasicDialog} this
32250      */
32251     setContentSize : function(w, h){
32252         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32253         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32254         //if(!this.el.isBorderBox()){
32255             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32256             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32257         //}
32258         if(this.tabs){
32259             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32260             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32261         }
32262         this.resizeTo(w, h);
32263         return this;
32264     },
32265
32266     /**
32267      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32268      * executed in response to a particular key being pressed while the dialog is active.
32269      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32270      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32271      * @param {Function} fn The function to call
32272      * @param {Object} scope (optional) The scope of the function
32273      * @return {Roo.BasicDialog} this
32274      */
32275     addKeyListener : function(key, fn, scope){
32276         var keyCode, shift, ctrl, alt;
32277         if(typeof key == "object" && !(key instanceof Array)){
32278             keyCode = key["key"];
32279             shift = key["shift"];
32280             ctrl = key["ctrl"];
32281             alt = key["alt"];
32282         }else{
32283             keyCode = key;
32284         }
32285         var handler = function(dlg, e){
32286             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32287                 var k = e.getKey();
32288                 if(keyCode instanceof Array){
32289                     for(var i = 0, len = keyCode.length; i < len; i++){
32290                         if(keyCode[i] == k){
32291                           fn.call(scope || window, dlg, k, e);
32292                           return;
32293                         }
32294                     }
32295                 }else{
32296                     if(k == keyCode){
32297                         fn.call(scope || window, dlg, k, e);
32298                     }
32299                 }
32300             }
32301         };
32302         this.on("keydown", handler);
32303         return this;
32304     },
32305
32306     /**
32307      * Returns the TabPanel component (creates it if it doesn't exist).
32308      * Note: If you wish to simply check for the existence of tabs without creating them,
32309      * check for a null 'tabs' property.
32310      * @return {Roo.TabPanel} The tabs component
32311      */
32312     getTabs : function(){
32313         if(!this.tabs){
32314             this.el.addClass("x-dlg-auto-tabs");
32315             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32316             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32317         }
32318         return this.tabs;
32319     },
32320
32321     /**
32322      * Adds a button to the footer section of the dialog.
32323      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32324      * object or a valid Roo.DomHelper element config
32325      * @param {Function} handler The function called when the button is clicked
32326      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32327      * @return {Roo.Button} The new button
32328      */
32329     addButton : function(config, handler, scope){
32330         var dh = Roo.DomHelper;
32331         if(!this.footer){
32332             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32333         }
32334         if(!this.btnContainer){
32335             var tb = this.footer.createChild({
32336
32337                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32338                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32339             }, null, true);
32340             this.btnContainer = tb.firstChild.firstChild.firstChild;
32341         }
32342         var bconfig = {
32343             handler: handler,
32344             scope: scope,
32345             minWidth: this.minButtonWidth,
32346             hideParent:true
32347         };
32348         if(typeof config == "string"){
32349             bconfig.text = config;
32350         }else{
32351             if(config.tag){
32352                 bconfig.dhconfig = config;
32353             }else{
32354                 Roo.apply(bconfig, config);
32355             }
32356         }
32357         var fc = false;
32358         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32359             bconfig.position = Math.max(0, bconfig.position);
32360             fc = this.btnContainer.childNodes[bconfig.position];
32361         }
32362          
32363         var btn = new Roo.Button(
32364             fc ? 
32365                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32366                 : this.btnContainer.appendChild(document.createElement("td")),
32367             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32368             bconfig
32369         );
32370         this.syncBodyHeight();
32371         if(!this.buttons){
32372             /**
32373              * Array of all the buttons that have been added to this dialog via addButton
32374              * @type Array
32375              */
32376             this.buttons = [];
32377         }
32378         this.buttons.push(btn);
32379         return btn;
32380     },
32381
32382     /**
32383      * Sets the default button to be focused when the dialog is displayed.
32384      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32385      * @return {Roo.BasicDialog} this
32386      */
32387     setDefaultButton : function(btn){
32388         this.defaultButton = btn;
32389         return this;
32390     },
32391
32392     // private
32393     getHeaderFooterHeight : function(safe){
32394         var height = 0;
32395         if(this.header){
32396            height += this.header.getHeight();
32397         }
32398         if(this.footer){
32399            var fm = this.footer.getMargins();
32400             height += (this.footer.getHeight()+fm.top+fm.bottom);
32401         }
32402         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32403         height += this.centerBg.getPadding("tb");
32404         return height;
32405     },
32406
32407     // private
32408     syncBodyHeight : function()
32409     {
32410         var bd = this.body, // the text
32411             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32412             bw = this.bwrap;
32413         var height = this.size.height - this.getHeaderFooterHeight(false);
32414         bd.setHeight(height-bd.getMargins("tb"));
32415         var hh = this.header.getHeight();
32416         var h = this.size.height-hh;
32417         cb.setHeight(h);
32418         
32419         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32420         bw.setHeight(h-cb.getPadding("tb"));
32421         
32422         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32423         bd.setWidth(bw.getWidth(true));
32424         if(this.tabs){
32425             this.tabs.syncHeight();
32426             if(Roo.isIE){
32427                 this.tabs.el.repaint();
32428             }
32429         }
32430     },
32431
32432     /**
32433      * Restores the previous state of the dialog if Roo.state is configured.
32434      * @return {Roo.BasicDialog} this
32435      */
32436     restoreState : function(){
32437         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32438         if(box && box.width){
32439             this.xy = [box.x, box.y];
32440             this.resizeTo(box.width, box.height);
32441         }
32442         return this;
32443     },
32444
32445     // private
32446     beforeShow : function(){
32447         this.expand();
32448         if(this.fixedcenter){
32449             this.xy = this.el.getCenterXY(true);
32450         }
32451         if(this.modal){
32452             Roo.get(document.body).addClass("x-body-masked");
32453             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32454             this.mask.show();
32455         }
32456         this.constrainXY();
32457     },
32458
32459     // private
32460     animShow : function(){
32461         var b = Roo.get(this.animateTarget).getBox();
32462         this.proxy.setSize(b.width, b.height);
32463         this.proxy.setLocation(b.x, b.y);
32464         this.proxy.show();
32465         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32466                     true, .35, this.showEl.createDelegate(this));
32467     },
32468
32469     /**
32470      * Shows the dialog.
32471      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32472      * @return {Roo.BasicDialog} this
32473      */
32474     show : function(animateTarget){
32475         if (this.fireEvent("beforeshow", this) === false){
32476             return;
32477         }
32478         if(this.syncHeightBeforeShow){
32479             this.syncBodyHeight();
32480         }else if(this.firstShow){
32481             this.firstShow = false;
32482             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32483         }
32484         this.animateTarget = animateTarget || this.animateTarget;
32485         if(!this.el.isVisible()){
32486             this.beforeShow();
32487             if(this.animateTarget && Roo.get(this.animateTarget)){
32488                 this.animShow();
32489             }else{
32490                 this.showEl();
32491             }
32492         }
32493         return this;
32494     },
32495
32496     // private
32497     showEl : function(){
32498         this.proxy.hide();
32499         this.el.setXY(this.xy);
32500         this.el.show();
32501         this.adjustAssets(true);
32502         this.toFront();
32503         this.focus();
32504         // IE peekaboo bug - fix found by Dave Fenwick
32505         if(Roo.isIE){
32506             this.el.repaint();
32507         }
32508         this.fireEvent("show", this);
32509     },
32510
32511     /**
32512      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32513      * dialog itself will receive focus.
32514      */
32515     focus : function(){
32516         if(this.defaultButton){
32517             this.defaultButton.focus();
32518         }else{
32519             this.focusEl.focus();
32520         }
32521     },
32522
32523     // private
32524     constrainXY : function(){
32525         if(this.constraintoviewport !== false){
32526             if(!this.viewSize){
32527                 if(this.container){
32528                     var s = this.container.getSize();
32529                     this.viewSize = [s.width, s.height];
32530                 }else{
32531                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32532                 }
32533             }
32534             var s = Roo.get(this.container||document).getScroll();
32535
32536             var x = this.xy[0], y = this.xy[1];
32537             var w = this.size.width, h = this.size.height;
32538             var vw = this.viewSize[0], vh = this.viewSize[1];
32539             // only move it if it needs it
32540             var moved = false;
32541             // first validate right/bottom
32542             if(x + w > vw+s.left){
32543                 x = vw - w;
32544                 moved = true;
32545             }
32546             if(y + h > vh+s.top){
32547                 y = vh - h;
32548                 moved = true;
32549             }
32550             // then make sure top/left isn't negative
32551             if(x < s.left){
32552                 x = s.left;
32553                 moved = true;
32554             }
32555             if(y < s.top){
32556                 y = s.top;
32557                 moved = true;
32558             }
32559             if(moved){
32560                 // cache xy
32561                 this.xy = [x, y];
32562                 if(this.isVisible()){
32563                     this.el.setLocation(x, y);
32564                     this.adjustAssets();
32565                 }
32566             }
32567         }
32568     },
32569
32570     // private
32571     onDrag : function(){
32572         if(!this.proxyDrag){
32573             this.xy = this.el.getXY();
32574             this.adjustAssets();
32575         }
32576     },
32577
32578     // private
32579     adjustAssets : function(doShow){
32580         var x = this.xy[0], y = this.xy[1];
32581         var w = this.size.width, h = this.size.height;
32582         if(doShow === true){
32583             if(this.shadow){
32584                 this.shadow.show(this.el);
32585             }
32586             if(this.shim){
32587                 this.shim.show();
32588             }
32589         }
32590         if(this.shadow && this.shadow.isVisible()){
32591             this.shadow.show(this.el);
32592         }
32593         if(this.shim && this.shim.isVisible()){
32594             this.shim.setBounds(x, y, w, h);
32595         }
32596     },
32597
32598     // private
32599     adjustViewport : function(w, h){
32600         if(!w || !h){
32601             w = Roo.lib.Dom.getViewWidth();
32602             h = Roo.lib.Dom.getViewHeight();
32603         }
32604         // cache the size
32605         this.viewSize = [w, h];
32606         if(this.modal && this.mask.isVisible()){
32607             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32608             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32609         }
32610         if(this.isVisible()){
32611             this.constrainXY();
32612         }
32613     },
32614
32615     /**
32616      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32617      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32618      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32619      */
32620     destroy : function(removeEl){
32621         if(this.isVisible()){
32622             this.animateTarget = null;
32623             this.hide();
32624         }
32625         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32626         if(this.tabs){
32627             this.tabs.destroy(removeEl);
32628         }
32629         Roo.destroy(
32630              this.shim,
32631              this.proxy,
32632              this.resizer,
32633              this.close,
32634              this.mask
32635         );
32636         if(this.dd){
32637             this.dd.unreg();
32638         }
32639         if(this.buttons){
32640            for(var i = 0, len = this.buttons.length; i < len; i++){
32641                this.buttons[i].destroy();
32642            }
32643         }
32644         this.el.removeAllListeners();
32645         if(removeEl === true){
32646             this.el.update("");
32647             this.el.remove();
32648         }
32649         Roo.DialogManager.unregister(this);
32650     },
32651
32652     // private
32653     startMove : function(){
32654         if(this.proxyDrag){
32655             this.proxy.show();
32656         }
32657         if(this.constraintoviewport !== false){
32658             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32659         }
32660     },
32661
32662     // private
32663     endMove : function(){
32664         if(!this.proxyDrag){
32665             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32666         }else{
32667             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32668             this.proxy.hide();
32669         }
32670         this.refreshSize();
32671         this.adjustAssets();
32672         this.focus();
32673         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32674     },
32675
32676     /**
32677      * Brings this dialog to the front of any other visible dialogs
32678      * @return {Roo.BasicDialog} this
32679      */
32680     toFront : function(){
32681         Roo.DialogManager.bringToFront(this);
32682         return this;
32683     },
32684
32685     /**
32686      * Sends this dialog to the back (under) of any other visible dialogs
32687      * @return {Roo.BasicDialog} this
32688      */
32689     toBack : function(){
32690         Roo.DialogManager.sendToBack(this);
32691         return this;
32692     },
32693
32694     /**
32695      * Centers this dialog in the viewport
32696      * @return {Roo.BasicDialog} this
32697      */
32698     center : function(){
32699         var xy = this.el.getCenterXY(true);
32700         this.moveTo(xy[0], xy[1]);
32701         return this;
32702     },
32703
32704     /**
32705      * Moves the dialog's top-left corner to the specified point
32706      * @param {Number} x
32707      * @param {Number} y
32708      * @return {Roo.BasicDialog} this
32709      */
32710     moveTo : function(x, y){
32711         this.xy = [x,y];
32712         if(this.isVisible()){
32713             this.el.setXY(this.xy);
32714             this.adjustAssets();
32715         }
32716         return this;
32717     },
32718
32719     /**
32720      * Aligns the dialog to the specified element
32721      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32722      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32723      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32724      * @return {Roo.BasicDialog} this
32725      */
32726     alignTo : function(element, position, offsets){
32727         this.xy = this.el.getAlignToXY(element, position, offsets);
32728         if(this.isVisible()){
32729             this.el.setXY(this.xy);
32730             this.adjustAssets();
32731         }
32732         return this;
32733     },
32734
32735     /**
32736      * Anchors an element to another element and realigns it when the window is resized.
32737      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32738      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32739      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32740      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32741      * is a number, it is used as the buffer delay (defaults to 50ms).
32742      * @return {Roo.BasicDialog} this
32743      */
32744     anchorTo : function(el, alignment, offsets, monitorScroll){
32745         var action = function(){
32746             this.alignTo(el, alignment, offsets);
32747         };
32748         Roo.EventManager.onWindowResize(action, this);
32749         var tm = typeof monitorScroll;
32750         if(tm != 'undefined'){
32751             Roo.EventManager.on(window, 'scroll', action, this,
32752                 {buffer: tm == 'number' ? monitorScroll : 50});
32753         }
32754         action.call(this);
32755         return this;
32756     },
32757
32758     /**
32759      * Returns true if the dialog is visible
32760      * @return {Boolean}
32761      */
32762     isVisible : function(){
32763         return this.el.isVisible();
32764     },
32765
32766     // private
32767     animHide : function(callback){
32768         var b = Roo.get(this.animateTarget).getBox();
32769         this.proxy.show();
32770         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32771         this.el.hide();
32772         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32773                     this.hideEl.createDelegate(this, [callback]));
32774     },
32775
32776     /**
32777      * Hides the dialog.
32778      * @param {Function} callback (optional) Function to call when the dialog is hidden
32779      * @return {Roo.BasicDialog} this
32780      */
32781     hide : function(callback){
32782         if (this.fireEvent("beforehide", this) === false){
32783             return;
32784         }
32785         if(this.shadow){
32786             this.shadow.hide();
32787         }
32788         if(this.shim) {
32789           this.shim.hide();
32790         }
32791         // sometimes animateTarget seems to get set.. causing problems...
32792         // this just double checks..
32793         if(this.animateTarget && Roo.get(this.animateTarget)) {
32794            this.animHide(callback);
32795         }else{
32796             this.el.hide();
32797             this.hideEl(callback);
32798         }
32799         return this;
32800     },
32801
32802     // private
32803     hideEl : function(callback){
32804         this.proxy.hide();
32805         if(this.modal){
32806             this.mask.hide();
32807             Roo.get(document.body).removeClass("x-body-masked");
32808         }
32809         this.fireEvent("hide", this);
32810         if(typeof callback == "function"){
32811             callback();
32812         }
32813     },
32814
32815     // private
32816     hideAction : function(){
32817         this.setLeft("-10000px");
32818         this.setTop("-10000px");
32819         this.setStyle("visibility", "hidden");
32820     },
32821
32822     // private
32823     refreshSize : function(){
32824         this.size = this.el.getSize();
32825         this.xy = this.el.getXY();
32826         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
32827     },
32828
32829     // private
32830     // z-index is managed by the DialogManager and may be overwritten at any time
32831     setZIndex : function(index){
32832         if(this.modal){
32833             this.mask.setStyle("z-index", index);
32834         }
32835         if(this.shim){
32836             this.shim.setStyle("z-index", ++index);
32837         }
32838         if(this.shadow){
32839             this.shadow.setZIndex(++index);
32840         }
32841         this.el.setStyle("z-index", ++index);
32842         if(this.proxy){
32843             this.proxy.setStyle("z-index", ++index);
32844         }
32845         if(this.resizer){
32846             this.resizer.proxy.setStyle("z-index", ++index);
32847         }
32848
32849         this.lastZIndex = index;
32850     },
32851
32852     /**
32853      * Returns the element for this dialog
32854      * @return {Roo.Element} The underlying dialog Element
32855      */
32856     getEl : function(){
32857         return this.el;
32858     }
32859 });
32860
32861 /**
32862  * @class Roo.DialogManager
32863  * Provides global access to BasicDialogs that have been created and
32864  * support for z-indexing (layering) multiple open dialogs.
32865  */
32866 Roo.DialogManager = function(){
32867     var list = {};
32868     var accessList = [];
32869     var front = null;
32870
32871     // private
32872     var sortDialogs = function(d1, d2){
32873         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
32874     };
32875
32876     // private
32877     var orderDialogs = function(){
32878         accessList.sort(sortDialogs);
32879         var seed = Roo.DialogManager.zseed;
32880         for(var i = 0, len = accessList.length; i < len; i++){
32881             var dlg = accessList[i];
32882             if(dlg){
32883                 dlg.setZIndex(seed + (i*10));
32884             }
32885         }
32886     };
32887
32888     return {
32889         /**
32890          * The starting z-index for BasicDialogs (defaults to 9000)
32891          * @type Number The z-index value
32892          */
32893         zseed : 9000,
32894
32895         // private
32896         register : function(dlg){
32897             list[dlg.id] = dlg;
32898             accessList.push(dlg);
32899         },
32900
32901         // private
32902         unregister : function(dlg){
32903             delete list[dlg.id];
32904             var i=0;
32905             var len=0;
32906             if(!accessList.indexOf){
32907                 for(  i = 0, len = accessList.length; i < len; i++){
32908                     if(accessList[i] == dlg){
32909                         accessList.splice(i, 1);
32910                         return;
32911                     }
32912                 }
32913             }else{
32914                  i = accessList.indexOf(dlg);
32915                 if(i != -1){
32916                     accessList.splice(i, 1);
32917                 }
32918             }
32919         },
32920
32921         /**
32922          * Gets a registered dialog by id
32923          * @param {String/Object} id The id of the dialog or a dialog
32924          * @return {Roo.BasicDialog} this
32925          */
32926         get : function(id){
32927             return typeof id == "object" ? id : list[id];
32928         },
32929
32930         /**
32931          * Brings the specified dialog to the front
32932          * @param {String/Object} dlg The id of the dialog or a dialog
32933          * @return {Roo.BasicDialog} this
32934          */
32935         bringToFront : function(dlg){
32936             dlg = this.get(dlg);
32937             if(dlg != front){
32938                 front = dlg;
32939                 dlg._lastAccess = new Date().getTime();
32940                 orderDialogs();
32941             }
32942             return dlg;
32943         },
32944
32945         /**
32946          * Sends the specified dialog to the back
32947          * @param {String/Object} dlg The id of the dialog or a dialog
32948          * @return {Roo.BasicDialog} this
32949          */
32950         sendToBack : function(dlg){
32951             dlg = this.get(dlg);
32952             dlg._lastAccess = -(new Date().getTime());
32953             orderDialogs();
32954             return dlg;
32955         },
32956
32957         /**
32958          * Hides all dialogs
32959          */
32960         hideAll : function(){
32961             for(var id in list){
32962                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
32963                     list[id].hide();
32964                 }
32965             }
32966         }
32967     };
32968 }();
32969
32970 /**
32971  * @class Roo.LayoutDialog
32972  * @extends Roo.BasicDialog
32973  * Dialog which provides adjustments for working with a layout in a Dialog.
32974  * Add your necessary layout config options to the dialog's config.<br>
32975  * Example usage (including a nested layout):
32976  * <pre><code>
32977 if(!dialog){
32978     dialog = new Roo.LayoutDialog("download-dlg", {
32979         modal: true,
32980         width:600,
32981         height:450,
32982         shadow:true,
32983         minWidth:500,
32984         minHeight:350,
32985         autoTabs:true,
32986         proxyDrag:true,
32987         // layout config merges with the dialog config
32988         center:{
32989             tabPosition: "top",
32990             alwaysShowTabs: true
32991         }
32992     });
32993     dialog.addKeyListener(27, dialog.hide, dialog);
32994     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
32995     dialog.addButton("Build It!", this.getDownload, this);
32996
32997     // we can even add nested layouts
32998     var innerLayout = new Roo.BorderLayout("dl-inner", {
32999         east: {
33000             initialSize: 200,
33001             autoScroll:true,
33002             split:true
33003         },
33004         center: {
33005             autoScroll:true
33006         }
33007     });
33008     innerLayout.beginUpdate();
33009     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33010     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33011     innerLayout.endUpdate(true);
33012
33013     var layout = dialog.getLayout();
33014     layout.beginUpdate();
33015     layout.add("center", new Roo.ContentPanel("standard-panel",
33016                         {title: "Download the Source", fitToFrame:true}));
33017     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33018                {title: "Build your own roo.js"}));
33019     layout.getRegion("center").showPanel(sp);
33020     layout.endUpdate();
33021 }
33022 </code></pre>
33023     * @constructor
33024     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33025     * @param {Object} config configuration options
33026   */
33027 Roo.LayoutDialog = function(el, cfg){
33028     
33029     var config=  cfg;
33030     if (typeof(cfg) == 'undefined') {
33031         config = Roo.apply({}, el);
33032         // not sure why we use documentElement here.. - it should always be body.
33033         // IE7 borks horribly if we use documentElement.
33034         // webkit also does not like documentElement - it creates a body element...
33035         el = Roo.get( document.body || document.documentElement ).createChild();
33036         //config.autoCreate = true;
33037     }
33038     
33039     
33040     config.autoTabs = false;
33041     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33042     this.body.setStyle({overflow:"hidden", position:"relative"});
33043     this.layout = new Roo.BorderLayout(this.body.dom, config);
33044     this.layout.monitorWindowResize = false;
33045     this.el.addClass("x-dlg-auto-layout");
33046     // fix case when center region overwrites center function
33047     this.center = Roo.BasicDialog.prototype.center;
33048     this.on("show", this.layout.layout, this.layout, true);
33049     if (config.items) {
33050         var xitems = config.items;
33051         delete config.items;
33052         Roo.each(xitems, this.addxtype, this);
33053     }
33054     
33055     
33056 };
33057 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33058     /**
33059      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33060      * @deprecated
33061      */
33062     endUpdate : function(){
33063         this.layout.endUpdate();
33064     },
33065
33066     /**
33067      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33068      *  @deprecated
33069      */
33070     beginUpdate : function(){
33071         this.layout.beginUpdate();
33072     },
33073
33074     /**
33075      * Get the BorderLayout for this dialog
33076      * @return {Roo.BorderLayout}
33077      */
33078     getLayout : function(){
33079         return this.layout;
33080     },
33081
33082     showEl : function(){
33083         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33084         if(Roo.isIE7){
33085             this.layout.layout();
33086         }
33087     },
33088
33089     // private
33090     // Use the syncHeightBeforeShow config option to control this automatically
33091     syncBodyHeight : function(){
33092         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33093         if(this.layout){this.layout.layout();}
33094     },
33095     
33096       /**
33097      * Add an xtype element (actually adds to the layout.)
33098      * @return {Object} xdata xtype object data.
33099      */
33100     
33101     addxtype : function(c) {
33102         return this.layout.addxtype(c);
33103     }
33104 });/*
33105  * Based on:
33106  * Ext JS Library 1.1.1
33107  * Copyright(c) 2006-2007, Ext JS, LLC.
33108  *
33109  * Originally Released Under LGPL - original licence link has changed is not relivant.
33110  *
33111  * Fork - LGPL
33112  * <script type="text/javascript">
33113  */
33114  
33115 /**
33116  * @class Roo.MessageBox
33117  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33118  * Example usage:
33119  *<pre><code>
33120 // Basic alert:
33121 Roo.Msg.alert('Status', 'Changes saved successfully.');
33122
33123 // Prompt for user data:
33124 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33125     if (btn == 'ok'){
33126         // process text value...
33127     }
33128 });
33129
33130 // Show a dialog using config options:
33131 Roo.Msg.show({
33132    title:'Save Changes?',
33133    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33134    buttons: Roo.Msg.YESNOCANCEL,
33135    fn: processResult,
33136    animEl: 'elId'
33137 });
33138 </code></pre>
33139  * @singleton
33140  */
33141 Roo.MessageBox = function(){
33142     var dlg, opt, mask, waitTimer;
33143     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33144     var buttons, activeTextEl, bwidth;
33145
33146     // private
33147     var handleButton = function(button){
33148         dlg.hide();
33149         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33150     };
33151
33152     // private
33153     var handleHide = function(){
33154         if(opt && opt.cls){
33155             dlg.el.removeClass(opt.cls);
33156         }
33157         if(waitTimer){
33158             Roo.TaskMgr.stop(waitTimer);
33159             waitTimer = null;
33160         }
33161     };
33162
33163     // private
33164     var updateButtons = function(b){
33165         var width = 0;
33166         if(!b){
33167             buttons["ok"].hide();
33168             buttons["cancel"].hide();
33169             buttons["yes"].hide();
33170             buttons["no"].hide();
33171             dlg.footer.dom.style.display = 'none';
33172             return width;
33173         }
33174         dlg.footer.dom.style.display = '';
33175         for(var k in buttons){
33176             if(typeof buttons[k] != "function"){
33177                 if(b[k]){
33178                     buttons[k].show();
33179                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33180                     width += buttons[k].el.getWidth()+15;
33181                 }else{
33182                     buttons[k].hide();
33183                 }
33184             }
33185         }
33186         return width;
33187     };
33188
33189     // private
33190     var handleEsc = function(d, k, e){
33191         if(opt && opt.closable !== false){
33192             dlg.hide();
33193         }
33194         if(e){
33195             e.stopEvent();
33196         }
33197     };
33198
33199     return {
33200         /**
33201          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33202          * @return {Roo.BasicDialog} The BasicDialog element
33203          */
33204         getDialog : function(){
33205            if(!dlg){
33206                 dlg = new Roo.BasicDialog("x-msg-box", {
33207                     autoCreate : true,
33208                     shadow: true,
33209                     draggable: true,
33210                     resizable:false,
33211                     constraintoviewport:false,
33212                     fixedcenter:true,
33213                     collapsible : false,
33214                     shim:true,
33215                     modal: true,
33216                     width:400, height:100,
33217                     buttonAlign:"center",
33218                     closeClick : function(){
33219                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33220                             handleButton("no");
33221                         }else{
33222                             handleButton("cancel");
33223                         }
33224                     }
33225                 });
33226                 dlg.on("hide", handleHide);
33227                 mask = dlg.mask;
33228                 dlg.addKeyListener(27, handleEsc);
33229                 buttons = {};
33230                 var bt = this.buttonText;
33231                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33232                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33233                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33234                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33235                 bodyEl = dlg.body.createChild({
33236
33237                     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>'
33238                 });
33239                 msgEl = bodyEl.dom.firstChild;
33240                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33241                 textboxEl.enableDisplayMode();
33242                 textboxEl.addKeyListener([10,13], function(){
33243                     if(dlg.isVisible() && opt && opt.buttons){
33244                         if(opt.buttons.ok){
33245                             handleButton("ok");
33246                         }else if(opt.buttons.yes){
33247                             handleButton("yes");
33248                         }
33249                     }
33250                 });
33251                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33252                 textareaEl.enableDisplayMode();
33253                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33254                 progressEl.enableDisplayMode();
33255                 var pf = progressEl.dom.firstChild;
33256                 if (pf) {
33257                     pp = Roo.get(pf.firstChild);
33258                     pp.setHeight(pf.offsetHeight);
33259                 }
33260                 
33261             }
33262             return dlg;
33263         },
33264
33265         /**
33266          * Updates the message box body text
33267          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33268          * the XHTML-compliant non-breaking space character '&amp;#160;')
33269          * @return {Roo.MessageBox} This message box
33270          */
33271         updateText : function(text){
33272             if(!dlg.isVisible() && !opt.width){
33273                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33274             }
33275             msgEl.innerHTML = text || '&#160;';
33276       
33277             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33278             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33279             var w = Math.max(
33280                     Math.min(opt.width || cw , this.maxWidth), 
33281                     Math.max(opt.minWidth || this.minWidth, bwidth)
33282             );
33283             if(opt.prompt){
33284                 activeTextEl.setWidth(w);
33285             }
33286             if(dlg.isVisible()){
33287                 dlg.fixedcenter = false;
33288             }
33289             // to big, make it scroll. = But as usual stupid IE does not support
33290             // !important..
33291             
33292             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33293                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33294                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33295             } else {
33296                 bodyEl.dom.style.height = '';
33297                 bodyEl.dom.style.overflowY = '';
33298             }
33299             if (cw > w) {
33300                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33301             } else {
33302                 bodyEl.dom.style.overflowX = '';
33303             }
33304             
33305             dlg.setContentSize(w, bodyEl.getHeight());
33306             if(dlg.isVisible()){
33307                 dlg.fixedcenter = true;
33308             }
33309             return this;
33310         },
33311
33312         /**
33313          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33314          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33315          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33316          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33317          * @return {Roo.MessageBox} This message box
33318          */
33319         updateProgress : function(value, text){
33320             if(text){
33321                 this.updateText(text);
33322             }
33323             if (pp) { // weird bug on my firefox - for some reason this is not defined
33324                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33325             }
33326             return this;
33327         },        
33328
33329         /**
33330          * Returns true if the message box is currently displayed
33331          * @return {Boolean} True if the message box is visible, else false
33332          */
33333         isVisible : function(){
33334             return dlg && dlg.isVisible();  
33335         },
33336
33337         /**
33338          * Hides the message box if it is displayed
33339          */
33340         hide : function(){
33341             if(this.isVisible()){
33342                 dlg.hide();
33343             }  
33344         },
33345
33346         /**
33347          * Displays a new message box, or reinitializes an existing message box, based on the config options
33348          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33349          * The following config object properties are supported:
33350          * <pre>
33351 Property    Type             Description
33352 ----------  ---------------  ------------------------------------------------------------------------------------
33353 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33354                                    closes (defaults to undefined)
33355 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33356                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33357 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33358                                    progress and wait dialogs will ignore this property and always hide the
33359                                    close button as they can only be closed programmatically.
33360 cls               String           A custom CSS class to apply to the message box element
33361 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33362                                    displayed (defaults to 75)
33363 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33364                                    function will be btn (the name of the button that was clicked, if applicable,
33365                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33366                                    Progress and wait dialogs will ignore this option since they do not respond to
33367                                    user actions and can only be closed programmatically, so any required function
33368                                    should be called by the same code after it closes the dialog.
33369 icon              String           A CSS class that provides a background image to be used as an icon for
33370                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33371 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33372 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33373 modal             Boolean          False to allow user interaction with the page while the message box is
33374                                    displayed (defaults to true)
33375 msg               String           A string that will replace the existing message box body text (defaults
33376                                    to the XHTML-compliant non-breaking space character '&#160;')
33377 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33378 progress          Boolean          True to display a progress bar (defaults to false)
33379 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33380 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33381 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33382 title             String           The title text
33383 value             String           The string value to set into the active textbox element if displayed
33384 wait              Boolean          True to display a progress bar (defaults to false)
33385 width             Number           The width of the dialog in pixels
33386 </pre>
33387          *
33388          * Example usage:
33389          * <pre><code>
33390 Roo.Msg.show({
33391    title: 'Address',
33392    msg: 'Please enter your address:',
33393    width: 300,
33394    buttons: Roo.MessageBox.OKCANCEL,
33395    multiline: true,
33396    fn: saveAddress,
33397    animEl: 'addAddressBtn'
33398 });
33399 </code></pre>
33400          * @param {Object} config Configuration options
33401          * @return {Roo.MessageBox} This message box
33402          */
33403         show : function(options)
33404         {
33405             
33406             // this causes nightmares if you show one dialog after another
33407             // especially on callbacks..
33408              
33409             if(this.isVisible()){
33410                 
33411                 this.hide();
33412                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33413                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33414                 Roo.log("New Dialog Message:" +  options.msg )
33415                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33416                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33417                 
33418             }
33419             var d = this.getDialog();
33420             opt = options;
33421             d.setTitle(opt.title || "&#160;");
33422             d.close.setDisplayed(opt.closable !== false);
33423             activeTextEl = textboxEl;
33424             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33425             if(opt.prompt){
33426                 if(opt.multiline){
33427                     textboxEl.hide();
33428                     textareaEl.show();
33429                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33430                         opt.multiline : this.defaultTextHeight);
33431                     activeTextEl = textareaEl;
33432                 }else{
33433                     textboxEl.show();
33434                     textareaEl.hide();
33435                 }
33436             }else{
33437                 textboxEl.hide();
33438                 textareaEl.hide();
33439             }
33440             progressEl.setDisplayed(opt.progress === true);
33441             this.updateProgress(0);
33442             activeTextEl.dom.value = opt.value || "";
33443             if(opt.prompt){
33444                 dlg.setDefaultButton(activeTextEl);
33445             }else{
33446                 var bs = opt.buttons;
33447                 var db = null;
33448                 if(bs && bs.ok){
33449                     db = buttons["ok"];
33450                 }else if(bs && bs.yes){
33451                     db = buttons["yes"];
33452                 }
33453                 dlg.setDefaultButton(db);
33454             }
33455             bwidth = updateButtons(opt.buttons);
33456             this.updateText(opt.msg);
33457             if(opt.cls){
33458                 d.el.addClass(opt.cls);
33459             }
33460             d.proxyDrag = opt.proxyDrag === true;
33461             d.modal = opt.modal !== false;
33462             d.mask = opt.modal !== false ? mask : false;
33463             if(!d.isVisible()){
33464                 // force it to the end of the z-index stack so it gets a cursor in FF
33465                 document.body.appendChild(dlg.el.dom);
33466                 d.animateTarget = null;
33467                 d.show(options.animEl);
33468             }
33469             return this;
33470         },
33471
33472         /**
33473          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33474          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33475          * and closing the message box when the process is complete.
33476          * @param {String} title The title bar text
33477          * @param {String} msg The message box body text
33478          * @return {Roo.MessageBox} This message box
33479          */
33480         progress : function(title, msg){
33481             this.show({
33482                 title : title,
33483                 msg : msg,
33484                 buttons: false,
33485                 progress:true,
33486                 closable:false,
33487                 minWidth: this.minProgressWidth,
33488                 modal : true
33489             });
33490             return this;
33491         },
33492
33493         /**
33494          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33495          * If a callback function is passed it will be called after the user clicks the button, and the
33496          * id of the button that was clicked will be passed as the only parameter to the callback
33497          * (could also be the top-right close button).
33498          * @param {String} title The title bar text
33499          * @param {String} msg The message box body text
33500          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33501          * @param {Object} scope (optional) The scope of the callback function
33502          * @return {Roo.MessageBox} This message box
33503          */
33504         alert : function(title, msg, fn, scope){
33505             this.show({
33506                 title : title,
33507                 msg : msg,
33508                 buttons: this.OK,
33509                 fn: fn,
33510                 scope : scope,
33511                 modal : true
33512             });
33513             return this;
33514         },
33515
33516         /**
33517          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33518          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33519          * You are responsible for closing the message box when the process is complete.
33520          * @param {String} msg The message box body text
33521          * @param {String} title (optional) The title bar text
33522          * @return {Roo.MessageBox} This message box
33523          */
33524         wait : function(msg, title){
33525             this.show({
33526                 title : title,
33527                 msg : msg,
33528                 buttons: false,
33529                 closable:false,
33530                 progress:true,
33531                 modal:true,
33532                 width:300,
33533                 wait:true
33534             });
33535             waitTimer = Roo.TaskMgr.start({
33536                 run: function(i){
33537                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33538                 },
33539                 interval: 1000
33540             });
33541             return this;
33542         },
33543
33544         /**
33545          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33546          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33547          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33548          * @param {String} title The title bar text
33549          * @param {String} msg The message box body text
33550          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33551          * @param {Object} scope (optional) The scope of the callback function
33552          * @return {Roo.MessageBox} This message box
33553          */
33554         confirm : function(title, msg, fn, scope){
33555             this.show({
33556                 title : title,
33557                 msg : msg,
33558                 buttons: this.YESNO,
33559                 fn: fn,
33560                 scope : scope,
33561                 modal : true
33562             });
33563             return this;
33564         },
33565
33566         /**
33567          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33568          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33569          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33570          * (could also be the top-right close button) and the text that was entered will be passed as the two
33571          * parameters to the callback.
33572          * @param {String} title The title bar text
33573          * @param {String} msg The message box body text
33574          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33575          * @param {Object} scope (optional) The scope of the callback function
33576          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33577          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33578          * @return {Roo.MessageBox} This message box
33579          */
33580         prompt : function(title, msg, fn, scope, multiline){
33581             this.show({
33582                 title : title,
33583                 msg : msg,
33584                 buttons: this.OKCANCEL,
33585                 fn: fn,
33586                 minWidth:250,
33587                 scope : scope,
33588                 prompt:true,
33589                 multiline: multiline,
33590                 modal : true
33591             });
33592             return this;
33593         },
33594
33595         /**
33596          * Button config that displays a single OK button
33597          * @type Object
33598          */
33599         OK : {ok:true},
33600         /**
33601          * Button config that displays Yes and No buttons
33602          * @type Object
33603          */
33604         YESNO : {yes:true, no:true},
33605         /**
33606          * Button config that displays OK and Cancel buttons
33607          * @type Object
33608          */
33609         OKCANCEL : {ok:true, cancel:true},
33610         /**
33611          * Button config that displays Yes, No and Cancel buttons
33612          * @type Object
33613          */
33614         YESNOCANCEL : {yes:true, no:true, cancel:true},
33615
33616         /**
33617          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33618          * @type Number
33619          */
33620         defaultTextHeight : 75,
33621         /**
33622          * The maximum width in pixels of the message box (defaults to 600)
33623          * @type Number
33624          */
33625         maxWidth : 600,
33626         /**
33627          * The minimum width in pixels of the message box (defaults to 100)
33628          * @type Number
33629          */
33630         minWidth : 100,
33631         /**
33632          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33633          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33634          * @type Number
33635          */
33636         minProgressWidth : 250,
33637         /**
33638          * An object containing the default button text strings that can be overriden for localized language support.
33639          * Supported properties are: ok, cancel, yes and no.
33640          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33641          * @type Object
33642          */
33643         buttonText : {
33644             ok : "OK",
33645             cancel : "Cancel",
33646             yes : "Yes",
33647             no : "No"
33648         }
33649     };
33650 }();
33651
33652 /**
33653  * Shorthand for {@link Roo.MessageBox}
33654  */
33655 Roo.Msg = Roo.MessageBox;/*
33656  * Based on:
33657  * Ext JS Library 1.1.1
33658  * Copyright(c) 2006-2007, Ext JS, LLC.
33659  *
33660  * Originally Released Under LGPL - original licence link has changed is not relivant.
33661  *
33662  * Fork - LGPL
33663  * <script type="text/javascript">
33664  */
33665 /**
33666  * @class Roo.QuickTips
33667  * Provides attractive and customizable tooltips for any element.
33668  * @singleton
33669  */
33670 Roo.QuickTips = function(){
33671     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33672     var ce, bd, xy, dd;
33673     var visible = false, disabled = true, inited = false;
33674     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33675     
33676     var onOver = function(e){
33677         if(disabled){
33678             return;
33679         }
33680         var t = e.getTarget();
33681         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33682             return;
33683         }
33684         if(ce && t == ce.el){
33685             clearTimeout(hideProc);
33686             return;
33687         }
33688         if(t && tagEls[t.id]){
33689             tagEls[t.id].el = t;
33690             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33691             return;
33692         }
33693         var ttp, et = Roo.fly(t);
33694         var ns = cfg.namespace;
33695         if(tm.interceptTitles && t.title){
33696             ttp = t.title;
33697             t.qtip = ttp;
33698             t.removeAttribute("title");
33699             e.preventDefault();
33700         }else{
33701             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33702         }
33703         if(ttp){
33704             showProc = show.defer(tm.showDelay, tm, [{
33705                 el: t, 
33706                 text: ttp.replace(/\\n/g,'<br/>'),
33707                 width: et.getAttributeNS(ns, cfg.width),
33708                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33709                 title: et.getAttributeNS(ns, cfg.title),
33710                     cls: et.getAttributeNS(ns, cfg.cls)
33711             }]);
33712         }
33713     };
33714     
33715     var onOut = function(e){
33716         clearTimeout(showProc);
33717         var t = e.getTarget();
33718         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33719             hideProc = setTimeout(hide, tm.hideDelay);
33720         }
33721     };
33722     
33723     var onMove = function(e){
33724         if(disabled){
33725             return;
33726         }
33727         xy = e.getXY();
33728         xy[1] += 18;
33729         if(tm.trackMouse && ce){
33730             el.setXY(xy);
33731         }
33732     };
33733     
33734     var onDown = function(e){
33735         clearTimeout(showProc);
33736         clearTimeout(hideProc);
33737         if(!e.within(el)){
33738             if(tm.hideOnClick){
33739                 hide();
33740                 tm.disable();
33741                 tm.enable.defer(100, tm);
33742             }
33743         }
33744     };
33745     
33746     var getPad = function(){
33747         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33748     };
33749
33750     var show = function(o){
33751         if(disabled){
33752             return;
33753         }
33754         clearTimeout(dismissProc);
33755         ce = o;
33756         if(removeCls){ // in case manually hidden
33757             el.removeClass(removeCls);
33758             removeCls = null;
33759         }
33760         if(ce.cls){
33761             el.addClass(ce.cls);
33762             removeCls = ce.cls;
33763         }
33764         if(ce.title){
33765             tipTitle.update(ce.title);
33766             tipTitle.show();
33767         }else{
33768             tipTitle.update('');
33769             tipTitle.hide();
33770         }
33771         el.dom.style.width  = tm.maxWidth+'px';
33772         //tipBody.dom.style.width = '';
33773         tipBodyText.update(o.text);
33774         var p = getPad(), w = ce.width;
33775         if(!w){
33776             var td = tipBodyText.dom;
33777             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33778             if(aw > tm.maxWidth){
33779                 w = tm.maxWidth;
33780             }else if(aw < tm.minWidth){
33781                 w = tm.minWidth;
33782             }else{
33783                 w = aw;
33784             }
33785         }
33786         //tipBody.setWidth(w);
33787         el.setWidth(parseInt(w, 10) + p);
33788         if(ce.autoHide === false){
33789             close.setDisplayed(true);
33790             if(dd){
33791                 dd.unlock();
33792             }
33793         }else{
33794             close.setDisplayed(false);
33795             if(dd){
33796                 dd.lock();
33797             }
33798         }
33799         if(xy){
33800             el.avoidY = xy[1]-18;
33801             el.setXY(xy);
33802         }
33803         if(tm.animate){
33804             el.setOpacity(.1);
33805             el.setStyle("visibility", "visible");
33806             el.fadeIn({callback: afterShow});
33807         }else{
33808             afterShow();
33809         }
33810     };
33811     
33812     var afterShow = function(){
33813         if(ce){
33814             el.show();
33815             esc.enable();
33816             if(tm.autoDismiss && ce.autoHide !== false){
33817                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
33818             }
33819         }
33820     };
33821     
33822     var hide = function(noanim){
33823         clearTimeout(dismissProc);
33824         clearTimeout(hideProc);
33825         ce = null;
33826         if(el.isVisible()){
33827             esc.disable();
33828             if(noanim !== true && tm.animate){
33829                 el.fadeOut({callback: afterHide});
33830             }else{
33831                 afterHide();
33832             } 
33833         }
33834     };
33835     
33836     var afterHide = function(){
33837         el.hide();
33838         if(removeCls){
33839             el.removeClass(removeCls);
33840             removeCls = null;
33841         }
33842     };
33843     
33844     return {
33845         /**
33846         * @cfg {Number} minWidth
33847         * The minimum width of the quick tip (defaults to 40)
33848         */
33849        minWidth : 40,
33850         /**
33851         * @cfg {Number} maxWidth
33852         * The maximum width of the quick tip (defaults to 300)
33853         */
33854        maxWidth : 300,
33855         /**
33856         * @cfg {Boolean} interceptTitles
33857         * True to automatically use the element's DOM title value if available (defaults to false)
33858         */
33859        interceptTitles : false,
33860         /**
33861         * @cfg {Boolean} trackMouse
33862         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
33863         */
33864        trackMouse : false,
33865         /**
33866         * @cfg {Boolean} hideOnClick
33867         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
33868         */
33869        hideOnClick : true,
33870         /**
33871         * @cfg {Number} showDelay
33872         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
33873         */
33874        showDelay : 500,
33875         /**
33876         * @cfg {Number} hideDelay
33877         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
33878         */
33879        hideDelay : 200,
33880         /**
33881         * @cfg {Boolean} autoHide
33882         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
33883         * Used in conjunction with hideDelay.
33884         */
33885        autoHide : true,
33886         /**
33887         * @cfg {Boolean}
33888         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
33889         * (defaults to true).  Used in conjunction with autoDismissDelay.
33890         */
33891        autoDismiss : true,
33892         /**
33893         * @cfg {Number}
33894         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
33895         */
33896        autoDismissDelay : 5000,
33897        /**
33898         * @cfg {Boolean} animate
33899         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
33900         */
33901        animate : false,
33902
33903        /**
33904         * @cfg {String} title
33905         * Title text to display (defaults to '').  This can be any valid HTML markup.
33906         */
33907         title: '',
33908        /**
33909         * @cfg {String} text
33910         * Body text to display (defaults to '').  This can be any valid HTML markup.
33911         */
33912         text : '',
33913        /**
33914         * @cfg {String} cls
33915         * A CSS class to apply to the base quick tip element (defaults to '').
33916         */
33917         cls : '',
33918        /**
33919         * @cfg {Number} width
33920         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
33921         * minWidth or maxWidth.
33922         */
33923         width : null,
33924
33925     /**
33926      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
33927      * or display QuickTips in a page.
33928      */
33929        init : function(){
33930           tm = Roo.QuickTips;
33931           cfg = tm.tagConfig;
33932           if(!inited){
33933               if(!Roo.isReady){ // allow calling of init() before onReady
33934                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
33935                   return;
33936               }
33937               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
33938               el.fxDefaults = {stopFx: true};
33939               // maximum custom styling
33940               //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>');
33941               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>');              
33942               tipTitle = el.child('h3');
33943               tipTitle.enableDisplayMode("block");
33944               tipBody = el.child('div.x-tip-bd');
33945               tipBodyText = el.child('div.x-tip-bd-inner');
33946               //bdLeft = el.child('div.x-tip-bd-left');
33947               //bdRight = el.child('div.x-tip-bd-right');
33948               close = el.child('div.x-tip-close');
33949               close.enableDisplayMode("block");
33950               close.on("click", hide);
33951               var d = Roo.get(document);
33952               d.on("mousedown", onDown);
33953               d.on("mouseover", onOver);
33954               d.on("mouseout", onOut);
33955               d.on("mousemove", onMove);
33956               esc = d.addKeyListener(27, hide);
33957               esc.disable();
33958               if(Roo.dd.DD){
33959                   dd = el.initDD("default", null, {
33960                       onDrag : function(){
33961                           el.sync();  
33962                       }
33963                   });
33964                   dd.setHandleElId(tipTitle.id);
33965                   dd.lock();
33966               }
33967               inited = true;
33968           }
33969           this.enable(); 
33970        },
33971
33972     /**
33973      * Configures a new quick tip instance and assigns it to a target element.  The following config options
33974      * are supported:
33975      * <pre>
33976 Property    Type                   Description
33977 ----------  ---------------------  ------------------------------------------------------------------------
33978 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
33979      * </ul>
33980      * @param {Object} config The config object
33981      */
33982        register : function(config){
33983            var cs = config instanceof Array ? config : arguments;
33984            for(var i = 0, len = cs.length; i < len; i++) {
33985                var c = cs[i];
33986                var target = c.target;
33987                if(target){
33988                    if(target instanceof Array){
33989                        for(var j = 0, jlen = target.length; j < jlen; j++){
33990                            tagEls[target[j]] = c;
33991                        }
33992                    }else{
33993                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
33994                    }
33995                }
33996            }
33997        },
33998
33999     /**
34000      * Removes this quick tip from its element and destroys it.
34001      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34002      */
34003        unregister : function(el){
34004            delete tagEls[Roo.id(el)];
34005        },
34006
34007     /**
34008      * Enable this quick tip.
34009      */
34010        enable : function(){
34011            if(inited && disabled){
34012                locks.pop();
34013                if(locks.length < 1){
34014                    disabled = false;
34015                }
34016            }
34017        },
34018
34019     /**
34020      * Disable this quick tip.
34021      */
34022        disable : function(){
34023           disabled = true;
34024           clearTimeout(showProc);
34025           clearTimeout(hideProc);
34026           clearTimeout(dismissProc);
34027           if(ce){
34028               hide(true);
34029           }
34030           locks.push(1);
34031        },
34032
34033     /**
34034      * Returns true if the quick tip is enabled, else false.
34035      */
34036        isEnabled : function(){
34037             return !disabled;
34038        },
34039
34040         // private
34041        tagConfig : {
34042            namespace : "roo", // was ext?? this may break..
34043            alt_namespace : "ext",
34044            attribute : "qtip",
34045            width : "width",
34046            target : "target",
34047            title : "qtitle",
34048            hide : "hide",
34049            cls : "qclass"
34050        }
34051    };
34052 }();
34053
34054 // backwards compat
34055 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34056  * Based on:
34057  * Ext JS Library 1.1.1
34058  * Copyright(c) 2006-2007, Ext JS, LLC.
34059  *
34060  * Originally Released Under LGPL - original licence link has changed is not relivant.
34061  *
34062  * Fork - LGPL
34063  * <script type="text/javascript">
34064  */
34065  
34066
34067 /**
34068  * @class Roo.tree.TreePanel
34069  * @extends Roo.data.Tree
34070
34071  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34072  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34073  * @cfg {Boolean} enableDD true to enable drag and drop
34074  * @cfg {Boolean} enableDrag true to enable just drag
34075  * @cfg {Boolean} enableDrop true to enable just drop
34076  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34077  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34078  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34079  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34080  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34081  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34082  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34083  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34084  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34085  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34086  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34087  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34088  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34089  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34090  * @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>
34091  * @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>
34092  * 
34093  * @constructor
34094  * @param {String/HTMLElement/Element} el The container element
34095  * @param {Object} config
34096  */
34097 Roo.tree.TreePanel = function(el, config){
34098     var root = false;
34099     var loader = false;
34100     if (config.root) {
34101         root = config.root;
34102         delete config.root;
34103     }
34104     if (config.loader) {
34105         loader = config.loader;
34106         delete config.loader;
34107     }
34108     
34109     Roo.apply(this, config);
34110     Roo.tree.TreePanel.superclass.constructor.call(this);
34111     this.el = Roo.get(el);
34112     this.el.addClass('x-tree');
34113     //console.log(root);
34114     if (root) {
34115         this.setRootNode( Roo.factory(root, Roo.tree));
34116     }
34117     if (loader) {
34118         this.loader = Roo.factory(loader, Roo.tree);
34119     }
34120    /**
34121     * Read-only. The id of the container element becomes this TreePanel's id.
34122     */
34123     this.id = this.el.id;
34124     this.addEvents({
34125         /**
34126         * @event beforeload
34127         * Fires before a node is loaded, return false to cancel
34128         * @param {Node} node The node being loaded
34129         */
34130         "beforeload" : true,
34131         /**
34132         * @event load
34133         * Fires when a node is loaded
34134         * @param {Node} node The node that was loaded
34135         */
34136         "load" : true,
34137         /**
34138         * @event textchange
34139         * Fires when the text for a node is changed
34140         * @param {Node} node The node
34141         * @param {String} text The new text
34142         * @param {String} oldText The old text
34143         */
34144         "textchange" : true,
34145         /**
34146         * @event beforeexpand
34147         * Fires before a node is expanded, return false to cancel.
34148         * @param {Node} node The node
34149         * @param {Boolean} deep
34150         * @param {Boolean} anim
34151         */
34152         "beforeexpand" : true,
34153         /**
34154         * @event beforecollapse
34155         * Fires before a node is collapsed, return false to cancel.
34156         * @param {Node} node The node
34157         * @param {Boolean} deep
34158         * @param {Boolean} anim
34159         */
34160         "beforecollapse" : true,
34161         /**
34162         * @event expand
34163         * Fires when a node is expanded
34164         * @param {Node} node The node
34165         */
34166         "expand" : true,
34167         /**
34168         * @event disabledchange
34169         * Fires when the disabled status of a node changes
34170         * @param {Node} node The node
34171         * @param {Boolean} disabled
34172         */
34173         "disabledchange" : true,
34174         /**
34175         * @event collapse
34176         * Fires when a node is collapsed
34177         * @param {Node} node The node
34178         */
34179         "collapse" : true,
34180         /**
34181         * @event beforeclick
34182         * Fires before click processing on a node. Return false to cancel the default action.
34183         * @param {Node} node The node
34184         * @param {Roo.EventObject} e The event object
34185         */
34186         "beforeclick":true,
34187         /**
34188         * @event checkchange
34189         * Fires when a node with a checkbox's checked property changes
34190         * @param {Node} this This node
34191         * @param {Boolean} checked
34192         */
34193         "checkchange":true,
34194         /**
34195         * @event click
34196         * Fires when a node is clicked
34197         * @param {Node} node The node
34198         * @param {Roo.EventObject} e The event object
34199         */
34200         "click":true,
34201         /**
34202         * @event dblclick
34203         * Fires when a node is double clicked
34204         * @param {Node} node The node
34205         * @param {Roo.EventObject} e The event object
34206         */
34207         "dblclick":true,
34208         /**
34209         * @event contextmenu
34210         * Fires when a node is right clicked
34211         * @param {Node} node The node
34212         * @param {Roo.EventObject} e The event object
34213         */
34214         "contextmenu":true,
34215         /**
34216         * @event beforechildrenrendered
34217         * Fires right before the child nodes for a node are rendered
34218         * @param {Node} node The node
34219         */
34220         "beforechildrenrendered":true,
34221         /**
34222         * @event startdrag
34223         * Fires when a node starts being dragged
34224         * @param {Roo.tree.TreePanel} this
34225         * @param {Roo.tree.TreeNode} node
34226         * @param {event} e The raw browser event
34227         */ 
34228        "startdrag" : true,
34229        /**
34230         * @event enddrag
34231         * Fires when a drag operation is complete
34232         * @param {Roo.tree.TreePanel} this
34233         * @param {Roo.tree.TreeNode} node
34234         * @param {event} e The raw browser event
34235         */
34236        "enddrag" : true,
34237        /**
34238         * @event dragdrop
34239         * Fires when a dragged node is dropped on a valid DD target
34240         * @param {Roo.tree.TreePanel} this
34241         * @param {Roo.tree.TreeNode} node
34242         * @param {DD} dd The dd it was dropped on
34243         * @param {event} e The raw browser event
34244         */
34245        "dragdrop" : true,
34246        /**
34247         * @event beforenodedrop
34248         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34249         * passed to handlers has the following properties:<br />
34250         * <ul style="padding:5px;padding-left:16px;">
34251         * <li>tree - The TreePanel</li>
34252         * <li>target - The node being targeted for the drop</li>
34253         * <li>data - The drag data from the drag source</li>
34254         * <li>point - The point of the drop - append, above or below</li>
34255         * <li>source - The drag source</li>
34256         * <li>rawEvent - Raw mouse event</li>
34257         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34258         * to be inserted by setting them on this object.</li>
34259         * <li>cancel - Set this to true to cancel the drop.</li>
34260         * </ul>
34261         * @param {Object} dropEvent
34262         */
34263        "beforenodedrop" : true,
34264        /**
34265         * @event nodedrop
34266         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34267         * passed to handlers has the following properties:<br />
34268         * <ul style="padding:5px;padding-left:16px;">
34269         * <li>tree - The TreePanel</li>
34270         * <li>target - The node being targeted for the drop</li>
34271         * <li>data - The drag data from the drag source</li>
34272         * <li>point - The point of the drop - append, above or below</li>
34273         * <li>source - The drag source</li>
34274         * <li>rawEvent - Raw mouse event</li>
34275         * <li>dropNode - Dropped node(s).</li>
34276         * </ul>
34277         * @param {Object} dropEvent
34278         */
34279        "nodedrop" : true,
34280         /**
34281         * @event nodedragover
34282         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34283         * passed to handlers has the following properties:<br />
34284         * <ul style="padding:5px;padding-left:16px;">
34285         * <li>tree - The TreePanel</li>
34286         * <li>target - The node being targeted for the drop</li>
34287         * <li>data - The drag data from the drag source</li>
34288         * <li>point - The point of the drop - append, above or below</li>
34289         * <li>source - The drag source</li>
34290         * <li>rawEvent - Raw mouse event</li>
34291         * <li>dropNode - Drop node(s) provided by the source.</li>
34292         * <li>cancel - Set this to true to signal drop not allowed.</li>
34293         * </ul>
34294         * @param {Object} dragOverEvent
34295         */
34296        "nodedragover" : true,
34297        /**
34298         * @event appendnode
34299         * Fires when append node to the tree
34300         * @param {Roo.tree.TreePanel} this
34301         * @param {Roo.tree.TreeNode} node
34302         * @param {Number} index The index of the newly appended node
34303         */
34304        "appendnode" : true
34305         
34306     });
34307     if(this.singleExpand){
34308        this.on("beforeexpand", this.restrictExpand, this);
34309     }
34310     if (this.editor) {
34311         this.editor.tree = this;
34312         this.editor = Roo.factory(this.editor, Roo.tree);
34313     }
34314     
34315     if (this.selModel) {
34316         this.selModel = Roo.factory(this.selModel, Roo.tree);
34317     }
34318    
34319 };
34320 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34321     rootVisible : true,
34322     animate: Roo.enableFx,
34323     lines : true,
34324     enableDD : false,
34325     hlDrop : Roo.enableFx,
34326   
34327     renderer: false,
34328     
34329     rendererTip: false,
34330     // private
34331     restrictExpand : function(node){
34332         var p = node.parentNode;
34333         if(p){
34334             if(p.expandedChild && p.expandedChild.parentNode == p){
34335                 p.expandedChild.collapse();
34336             }
34337             p.expandedChild = node;
34338         }
34339     },
34340
34341     // private override
34342     setRootNode : function(node){
34343         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34344         if(!this.rootVisible){
34345             node.ui = new Roo.tree.RootTreeNodeUI(node);
34346         }
34347         return node;
34348     },
34349
34350     /**
34351      * Returns the container element for this TreePanel
34352      */
34353     getEl : function(){
34354         return this.el;
34355     },
34356
34357     /**
34358      * Returns the default TreeLoader for this TreePanel
34359      */
34360     getLoader : function(){
34361         return this.loader;
34362     },
34363
34364     /**
34365      * Expand all nodes
34366      */
34367     expandAll : function(){
34368         this.root.expand(true);
34369     },
34370
34371     /**
34372      * Collapse all nodes
34373      */
34374     collapseAll : function(){
34375         this.root.collapse(true);
34376     },
34377
34378     /**
34379      * Returns the selection model used by this TreePanel
34380      */
34381     getSelectionModel : function(){
34382         if(!this.selModel){
34383             this.selModel = new Roo.tree.DefaultSelectionModel();
34384         }
34385         return this.selModel;
34386     },
34387
34388     /**
34389      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34390      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34391      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34392      * @return {Array}
34393      */
34394     getChecked : function(a, startNode){
34395         startNode = startNode || this.root;
34396         var r = [];
34397         var f = function(){
34398             if(this.attributes.checked){
34399                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34400             }
34401         }
34402         startNode.cascade(f);
34403         return r;
34404     },
34405
34406     /**
34407      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34408      * @param {String} path
34409      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34410      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34411      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34412      */
34413     expandPath : function(path, attr, callback){
34414         attr = attr || "id";
34415         var keys = path.split(this.pathSeparator);
34416         var curNode = this.root;
34417         if(curNode.attributes[attr] != keys[1]){ // invalid root
34418             if(callback){
34419                 callback(false, null);
34420             }
34421             return;
34422         }
34423         var index = 1;
34424         var f = function(){
34425             if(++index == keys.length){
34426                 if(callback){
34427                     callback(true, curNode);
34428                 }
34429                 return;
34430             }
34431             var c = curNode.findChild(attr, keys[index]);
34432             if(!c){
34433                 if(callback){
34434                     callback(false, curNode);
34435                 }
34436                 return;
34437             }
34438             curNode = c;
34439             c.expand(false, false, f);
34440         };
34441         curNode.expand(false, false, f);
34442     },
34443
34444     /**
34445      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34446      * @param {String} path
34447      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34448      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34449      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34450      */
34451     selectPath : function(path, attr, callback){
34452         attr = attr || "id";
34453         var keys = path.split(this.pathSeparator);
34454         var v = keys.pop();
34455         if(keys.length > 0){
34456             var f = function(success, node){
34457                 if(success && node){
34458                     var n = node.findChild(attr, v);
34459                     if(n){
34460                         n.select();
34461                         if(callback){
34462                             callback(true, n);
34463                         }
34464                     }else if(callback){
34465                         callback(false, n);
34466                     }
34467                 }else{
34468                     if(callback){
34469                         callback(false, n);
34470                     }
34471                 }
34472             };
34473             this.expandPath(keys.join(this.pathSeparator), attr, f);
34474         }else{
34475             this.root.select();
34476             if(callback){
34477                 callback(true, this.root);
34478             }
34479         }
34480     },
34481
34482     getTreeEl : function(){
34483         return this.el;
34484     },
34485
34486     /**
34487      * Trigger rendering of this TreePanel
34488      */
34489     render : function(){
34490         if (this.innerCt) {
34491             return this; // stop it rendering more than once!!
34492         }
34493         
34494         this.innerCt = this.el.createChild({tag:"ul",
34495                cls:"x-tree-root-ct " +
34496                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34497
34498         if(this.containerScroll){
34499             Roo.dd.ScrollManager.register(this.el);
34500         }
34501         if((this.enableDD || this.enableDrop) && !this.dropZone){
34502            /**
34503             * The dropZone used by this tree if drop is enabled
34504             * @type Roo.tree.TreeDropZone
34505             */
34506              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34507                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34508            });
34509         }
34510         if((this.enableDD || this.enableDrag) && !this.dragZone){
34511            /**
34512             * The dragZone used by this tree if drag is enabled
34513             * @type Roo.tree.TreeDragZone
34514             */
34515             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34516                ddGroup: this.ddGroup || "TreeDD",
34517                scroll: this.ddScroll
34518            });
34519         }
34520         this.getSelectionModel().init(this);
34521         if (!this.root) {
34522             Roo.log("ROOT not set in tree");
34523             return this;
34524         }
34525         this.root.render();
34526         if(!this.rootVisible){
34527             this.root.renderChildren();
34528         }
34529         return this;
34530     }
34531 });/*
34532  * Based on:
34533  * Ext JS Library 1.1.1
34534  * Copyright(c) 2006-2007, Ext JS, LLC.
34535  *
34536  * Originally Released Under LGPL - original licence link has changed is not relivant.
34537  *
34538  * Fork - LGPL
34539  * <script type="text/javascript">
34540  */
34541  
34542
34543 /**
34544  * @class Roo.tree.DefaultSelectionModel
34545  * @extends Roo.util.Observable
34546  * The default single selection for a TreePanel.
34547  * @param {Object} cfg Configuration
34548  */
34549 Roo.tree.DefaultSelectionModel = function(cfg){
34550    this.selNode = null;
34551    
34552    
34553    
34554    this.addEvents({
34555        /**
34556         * @event selectionchange
34557         * Fires when the selected node changes
34558         * @param {DefaultSelectionModel} this
34559         * @param {TreeNode} node the new selection
34560         */
34561        "selectionchange" : true,
34562
34563        /**
34564         * @event beforeselect
34565         * Fires before the selected node changes, return false to cancel the change
34566         * @param {DefaultSelectionModel} this
34567         * @param {TreeNode} node the new selection
34568         * @param {TreeNode} node the old selection
34569         */
34570        "beforeselect" : true
34571    });
34572    
34573     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34574 };
34575
34576 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34577     init : function(tree){
34578         this.tree = tree;
34579         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34580         tree.on("click", this.onNodeClick, this);
34581     },
34582     
34583     onNodeClick : function(node, e){
34584         if (e.ctrlKey && this.selNode == node)  {
34585             this.unselect(node);
34586             return;
34587         }
34588         this.select(node);
34589     },
34590     
34591     /**
34592      * Select a node.
34593      * @param {TreeNode} node The node to select
34594      * @return {TreeNode} The selected node
34595      */
34596     select : function(node){
34597         var last = this.selNode;
34598         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34599             if(last){
34600                 last.ui.onSelectedChange(false);
34601             }
34602             this.selNode = node;
34603             node.ui.onSelectedChange(true);
34604             this.fireEvent("selectionchange", this, node, last);
34605         }
34606         return node;
34607     },
34608     
34609     /**
34610      * Deselect a node.
34611      * @param {TreeNode} node The node to unselect
34612      */
34613     unselect : function(node){
34614         if(this.selNode == node){
34615             this.clearSelections();
34616         }    
34617     },
34618     
34619     /**
34620      * Clear all selections
34621      */
34622     clearSelections : function(){
34623         var n = this.selNode;
34624         if(n){
34625             n.ui.onSelectedChange(false);
34626             this.selNode = null;
34627             this.fireEvent("selectionchange", this, null);
34628         }
34629         return n;
34630     },
34631     
34632     /**
34633      * Get the selected node
34634      * @return {TreeNode} The selected node
34635      */
34636     getSelectedNode : function(){
34637         return this.selNode;    
34638     },
34639     
34640     /**
34641      * Returns true if the node is selected
34642      * @param {TreeNode} node The node to check
34643      * @return {Boolean}
34644      */
34645     isSelected : function(node){
34646         return this.selNode == node;  
34647     },
34648
34649     /**
34650      * Selects the node above the selected node in the tree, intelligently walking the nodes
34651      * @return TreeNode The new selection
34652      */
34653     selectPrevious : function(){
34654         var s = this.selNode || this.lastSelNode;
34655         if(!s){
34656             return null;
34657         }
34658         var ps = s.previousSibling;
34659         if(ps){
34660             if(!ps.isExpanded() || ps.childNodes.length < 1){
34661                 return this.select(ps);
34662             } else{
34663                 var lc = ps.lastChild;
34664                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34665                     lc = lc.lastChild;
34666                 }
34667                 return this.select(lc);
34668             }
34669         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34670             return this.select(s.parentNode);
34671         }
34672         return null;
34673     },
34674
34675     /**
34676      * Selects the node above the selected node in the tree, intelligently walking the nodes
34677      * @return TreeNode The new selection
34678      */
34679     selectNext : function(){
34680         var s = this.selNode || this.lastSelNode;
34681         if(!s){
34682             return null;
34683         }
34684         if(s.firstChild && s.isExpanded()){
34685              return this.select(s.firstChild);
34686          }else if(s.nextSibling){
34687              return this.select(s.nextSibling);
34688          }else if(s.parentNode){
34689             var newS = null;
34690             s.parentNode.bubble(function(){
34691                 if(this.nextSibling){
34692                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34693                     return false;
34694                 }
34695             });
34696             return newS;
34697          }
34698         return null;
34699     },
34700
34701     onKeyDown : function(e){
34702         var s = this.selNode || this.lastSelNode;
34703         // undesirable, but required
34704         var sm = this;
34705         if(!s){
34706             return;
34707         }
34708         var k = e.getKey();
34709         switch(k){
34710              case e.DOWN:
34711                  e.stopEvent();
34712                  this.selectNext();
34713              break;
34714              case e.UP:
34715                  e.stopEvent();
34716                  this.selectPrevious();
34717              break;
34718              case e.RIGHT:
34719                  e.preventDefault();
34720                  if(s.hasChildNodes()){
34721                      if(!s.isExpanded()){
34722                          s.expand();
34723                      }else if(s.firstChild){
34724                          this.select(s.firstChild, e);
34725                      }
34726                  }
34727              break;
34728              case e.LEFT:
34729                  e.preventDefault();
34730                  if(s.hasChildNodes() && s.isExpanded()){
34731                      s.collapse();
34732                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34733                      this.select(s.parentNode, e);
34734                  }
34735              break;
34736         };
34737     }
34738 });
34739
34740 /**
34741  * @class Roo.tree.MultiSelectionModel
34742  * @extends Roo.util.Observable
34743  * Multi selection for a TreePanel.
34744  * @param {Object} cfg Configuration
34745  */
34746 Roo.tree.MultiSelectionModel = function(){
34747    this.selNodes = [];
34748    this.selMap = {};
34749    this.addEvents({
34750        /**
34751         * @event selectionchange
34752         * Fires when the selected nodes change
34753         * @param {MultiSelectionModel} this
34754         * @param {Array} nodes Array of the selected nodes
34755         */
34756        "selectionchange" : true
34757    });
34758    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34759    
34760 };
34761
34762 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34763     init : function(tree){
34764         this.tree = tree;
34765         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34766         tree.on("click", this.onNodeClick, this);
34767     },
34768     
34769     onNodeClick : function(node, e){
34770         this.select(node, e, e.ctrlKey);
34771     },
34772     
34773     /**
34774      * Select a node.
34775      * @param {TreeNode} node The node to select
34776      * @param {EventObject} e (optional) An event associated with the selection
34777      * @param {Boolean} keepExisting True to retain existing selections
34778      * @return {TreeNode} The selected node
34779      */
34780     select : function(node, e, keepExisting){
34781         if(keepExisting !== true){
34782             this.clearSelections(true);
34783         }
34784         if(this.isSelected(node)){
34785             this.lastSelNode = node;
34786             return node;
34787         }
34788         this.selNodes.push(node);
34789         this.selMap[node.id] = node;
34790         this.lastSelNode = node;
34791         node.ui.onSelectedChange(true);
34792         this.fireEvent("selectionchange", this, this.selNodes);
34793         return node;
34794     },
34795     
34796     /**
34797      * Deselect a node.
34798      * @param {TreeNode} node The node to unselect
34799      */
34800     unselect : function(node){
34801         if(this.selMap[node.id]){
34802             node.ui.onSelectedChange(false);
34803             var sn = this.selNodes;
34804             var index = -1;
34805             if(sn.indexOf){
34806                 index = sn.indexOf(node);
34807             }else{
34808                 for(var i = 0, len = sn.length; i < len; i++){
34809                     if(sn[i] == node){
34810                         index = i;
34811                         break;
34812                     }
34813                 }
34814             }
34815             if(index != -1){
34816                 this.selNodes.splice(index, 1);
34817             }
34818             delete this.selMap[node.id];
34819             this.fireEvent("selectionchange", this, this.selNodes);
34820         }
34821     },
34822     
34823     /**
34824      * Clear all selections
34825      */
34826     clearSelections : function(suppressEvent){
34827         var sn = this.selNodes;
34828         if(sn.length > 0){
34829             for(var i = 0, len = sn.length; i < len; i++){
34830                 sn[i].ui.onSelectedChange(false);
34831             }
34832             this.selNodes = [];
34833             this.selMap = {};
34834             if(suppressEvent !== true){
34835                 this.fireEvent("selectionchange", this, this.selNodes);
34836             }
34837         }
34838     },
34839     
34840     /**
34841      * Returns true if the node is selected
34842      * @param {TreeNode} node The node to check
34843      * @return {Boolean}
34844      */
34845     isSelected : function(node){
34846         return this.selMap[node.id] ? true : false;  
34847     },
34848     
34849     /**
34850      * Returns an array of the selected nodes
34851      * @return {Array}
34852      */
34853     getSelectedNodes : function(){
34854         return this.selNodes;    
34855     },
34856
34857     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
34858
34859     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
34860
34861     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
34862 });/*
34863  * Based on:
34864  * Ext JS Library 1.1.1
34865  * Copyright(c) 2006-2007, Ext JS, LLC.
34866  *
34867  * Originally Released Under LGPL - original licence link has changed is not relivant.
34868  *
34869  * Fork - LGPL
34870  * <script type="text/javascript">
34871  */
34872  
34873 /**
34874  * @class Roo.tree.TreeNode
34875  * @extends Roo.data.Node
34876  * @cfg {String} text The text for this node
34877  * @cfg {Boolean} expanded true to start the node expanded
34878  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
34879  * @cfg {Boolean} allowDrop false if this node cannot be drop on
34880  * @cfg {Boolean} disabled true to start the node disabled
34881  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
34882  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
34883  * @cfg {String} cls A css class to be added to the node
34884  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
34885  * @cfg {String} href URL of the link used for the node (defaults to #)
34886  * @cfg {String} hrefTarget target frame for the link
34887  * @cfg {String} qtip An Ext QuickTip for the node
34888  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
34889  * @cfg {Boolean} singleClickExpand True for single click expand on this node
34890  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
34891  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
34892  * (defaults to undefined with no checkbox rendered)
34893  * @constructor
34894  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
34895  */
34896 Roo.tree.TreeNode = function(attributes){
34897     attributes = attributes || {};
34898     if(typeof attributes == "string"){
34899         attributes = {text: attributes};
34900     }
34901     this.childrenRendered = false;
34902     this.rendered = false;
34903     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
34904     this.expanded = attributes.expanded === true;
34905     this.isTarget = attributes.isTarget !== false;
34906     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
34907     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
34908
34909     /**
34910      * Read-only. The text for this node. To change it use setText().
34911      * @type String
34912      */
34913     this.text = attributes.text;
34914     /**
34915      * True if this node is disabled.
34916      * @type Boolean
34917      */
34918     this.disabled = attributes.disabled === true;
34919
34920     this.addEvents({
34921         /**
34922         * @event textchange
34923         * Fires when the text for this node is changed
34924         * @param {Node} this This node
34925         * @param {String} text The new text
34926         * @param {String} oldText The old text
34927         */
34928         "textchange" : true,
34929         /**
34930         * @event beforeexpand
34931         * Fires before this node is expanded, return false to cancel.
34932         * @param {Node} this This node
34933         * @param {Boolean} deep
34934         * @param {Boolean} anim
34935         */
34936         "beforeexpand" : true,
34937         /**
34938         * @event beforecollapse
34939         * Fires before this node is collapsed, return false to cancel.
34940         * @param {Node} this This node
34941         * @param {Boolean} deep
34942         * @param {Boolean} anim
34943         */
34944         "beforecollapse" : true,
34945         /**
34946         * @event expand
34947         * Fires when this node is expanded
34948         * @param {Node} this This node
34949         */
34950         "expand" : true,
34951         /**
34952         * @event disabledchange
34953         * Fires when the disabled status of this node changes
34954         * @param {Node} this This node
34955         * @param {Boolean} disabled
34956         */
34957         "disabledchange" : true,
34958         /**
34959         * @event collapse
34960         * Fires when this node is collapsed
34961         * @param {Node} this This node
34962         */
34963         "collapse" : true,
34964         /**
34965         * @event beforeclick
34966         * Fires before click processing. Return false to cancel the default action.
34967         * @param {Node} this This node
34968         * @param {Roo.EventObject} e The event object
34969         */
34970         "beforeclick":true,
34971         /**
34972         * @event checkchange
34973         * Fires when a node with a checkbox's checked property changes
34974         * @param {Node} this This node
34975         * @param {Boolean} checked
34976         */
34977         "checkchange":true,
34978         /**
34979         * @event click
34980         * Fires when this node is clicked
34981         * @param {Node} this This node
34982         * @param {Roo.EventObject} e The event object
34983         */
34984         "click":true,
34985         /**
34986         * @event dblclick
34987         * Fires when this node is double clicked
34988         * @param {Node} this This node
34989         * @param {Roo.EventObject} e The event object
34990         */
34991         "dblclick":true,
34992         /**
34993         * @event contextmenu
34994         * Fires when this node is right clicked
34995         * @param {Node} this This node
34996         * @param {Roo.EventObject} e The event object
34997         */
34998         "contextmenu":true,
34999         /**
35000         * @event beforechildrenrendered
35001         * Fires right before the child nodes for this node are rendered
35002         * @param {Node} this This node
35003         */
35004         "beforechildrenrendered":true
35005     });
35006
35007     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35008
35009     /**
35010      * Read-only. The UI for this node
35011      * @type TreeNodeUI
35012      */
35013     this.ui = new uiClass(this);
35014     
35015     // finally support items[]
35016     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35017         return;
35018     }
35019     
35020     
35021     Roo.each(this.attributes.items, function(c) {
35022         this.appendChild(Roo.factory(c,Roo.Tree));
35023     }, this);
35024     delete this.attributes.items;
35025     
35026     
35027     
35028 };
35029 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35030     preventHScroll: true,
35031     /**
35032      * Returns true if this node is expanded
35033      * @return {Boolean}
35034      */
35035     isExpanded : function(){
35036         return this.expanded;
35037     },
35038
35039     /**
35040      * Returns the UI object for this node
35041      * @return {TreeNodeUI}
35042      */
35043     getUI : function(){
35044         return this.ui;
35045     },
35046
35047     // private override
35048     setFirstChild : function(node){
35049         var of = this.firstChild;
35050         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35051         if(this.childrenRendered && of && node != of){
35052             of.renderIndent(true, true);
35053         }
35054         if(this.rendered){
35055             this.renderIndent(true, true);
35056         }
35057     },
35058
35059     // private override
35060     setLastChild : function(node){
35061         var ol = this.lastChild;
35062         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35063         if(this.childrenRendered && ol && node != ol){
35064             ol.renderIndent(true, true);
35065         }
35066         if(this.rendered){
35067             this.renderIndent(true, true);
35068         }
35069     },
35070
35071     // these methods are overridden to provide lazy rendering support
35072     // private override
35073     appendChild : function()
35074     {
35075         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35076         if(node && this.childrenRendered){
35077             node.render();
35078         }
35079         this.ui.updateExpandIcon();
35080         return node;
35081     },
35082
35083     // private override
35084     removeChild : function(node){
35085         this.ownerTree.getSelectionModel().unselect(node);
35086         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35087         // if it's been rendered remove dom node
35088         if(this.childrenRendered){
35089             node.ui.remove();
35090         }
35091         if(this.childNodes.length < 1){
35092             this.collapse(false, false);
35093         }else{
35094             this.ui.updateExpandIcon();
35095         }
35096         if(!this.firstChild) {
35097             this.childrenRendered = false;
35098         }
35099         return node;
35100     },
35101
35102     // private override
35103     insertBefore : function(node, refNode){
35104         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35105         if(newNode && refNode && this.childrenRendered){
35106             node.render();
35107         }
35108         this.ui.updateExpandIcon();
35109         return newNode;
35110     },
35111
35112     /**
35113      * Sets the text for this node
35114      * @param {String} text
35115      */
35116     setText : function(text){
35117         var oldText = this.text;
35118         this.text = text;
35119         this.attributes.text = text;
35120         if(this.rendered){ // event without subscribing
35121             this.ui.onTextChange(this, text, oldText);
35122         }
35123         this.fireEvent("textchange", this, text, oldText);
35124     },
35125
35126     /**
35127      * Triggers selection of this node
35128      */
35129     select : function(){
35130         this.getOwnerTree().getSelectionModel().select(this);
35131     },
35132
35133     /**
35134      * Triggers deselection of this node
35135      */
35136     unselect : function(){
35137         this.getOwnerTree().getSelectionModel().unselect(this);
35138     },
35139
35140     /**
35141      * Returns true if this node is selected
35142      * @return {Boolean}
35143      */
35144     isSelected : function(){
35145         return this.getOwnerTree().getSelectionModel().isSelected(this);
35146     },
35147
35148     /**
35149      * Expand this node.
35150      * @param {Boolean} deep (optional) True to expand all children as well
35151      * @param {Boolean} anim (optional) false to cancel the default animation
35152      * @param {Function} callback (optional) A callback to be called when
35153      * expanding this node completes (does not wait for deep expand to complete).
35154      * Called with 1 parameter, this node.
35155      */
35156     expand : function(deep, anim, callback){
35157         if(!this.expanded){
35158             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35159                 return;
35160             }
35161             if(!this.childrenRendered){
35162                 this.renderChildren();
35163             }
35164             this.expanded = true;
35165             
35166             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35167                 this.ui.animExpand(function(){
35168                     this.fireEvent("expand", this);
35169                     if(typeof callback == "function"){
35170                         callback(this);
35171                     }
35172                     if(deep === true){
35173                         this.expandChildNodes(true);
35174                     }
35175                 }.createDelegate(this));
35176                 return;
35177             }else{
35178                 this.ui.expand();
35179                 this.fireEvent("expand", this);
35180                 if(typeof callback == "function"){
35181                     callback(this);
35182                 }
35183             }
35184         }else{
35185            if(typeof callback == "function"){
35186                callback(this);
35187            }
35188         }
35189         if(deep === true){
35190             this.expandChildNodes(true);
35191         }
35192     },
35193
35194     isHiddenRoot : function(){
35195         return this.isRoot && !this.getOwnerTree().rootVisible;
35196     },
35197
35198     /**
35199      * Collapse this node.
35200      * @param {Boolean} deep (optional) True to collapse all children as well
35201      * @param {Boolean} anim (optional) false to cancel the default animation
35202      */
35203     collapse : function(deep, anim){
35204         if(this.expanded && !this.isHiddenRoot()){
35205             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35206                 return;
35207             }
35208             this.expanded = false;
35209             if((this.getOwnerTree().animate && anim !== false) || anim){
35210                 this.ui.animCollapse(function(){
35211                     this.fireEvent("collapse", this);
35212                     if(deep === true){
35213                         this.collapseChildNodes(true);
35214                     }
35215                 }.createDelegate(this));
35216                 return;
35217             }else{
35218                 this.ui.collapse();
35219                 this.fireEvent("collapse", this);
35220             }
35221         }
35222         if(deep === true){
35223             var cs = this.childNodes;
35224             for(var i = 0, len = cs.length; i < len; i++) {
35225                 cs[i].collapse(true, false);
35226             }
35227         }
35228     },
35229
35230     // private
35231     delayedExpand : function(delay){
35232         if(!this.expandProcId){
35233             this.expandProcId = this.expand.defer(delay, this);
35234         }
35235     },
35236
35237     // private
35238     cancelExpand : function(){
35239         if(this.expandProcId){
35240             clearTimeout(this.expandProcId);
35241         }
35242         this.expandProcId = false;
35243     },
35244
35245     /**
35246      * Toggles expanded/collapsed state of the node
35247      */
35248     toggle : function(){
35249         if(this.expanded){
35250             this.collapse();
35251         }else{
35252             this.expand();
35253         }
35254     },
35255
35256     /**
35257      * Ensures all parent nodes are expanded
35258      */
35259     ensureVisible : function(callback){
35260         var tree = this.getOwnerTree();
35261         tree.expandPath(this.parentNode.getPath(), false, function(){
35262             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35263             Roo.callback(callback);
35264         }.createDelegate(this));
35265     },
35266
35267     /**
35268      * Expand all child nodes
35269      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35270      */
35271     expandChildNodes : function(deep){
35272         var cs = this.childNodes;
35273         for(var i = 0, len = cs.length; i < len; i++) {
35274                 cs[i].expand(deep);
35275         }
35276     },
35277
35278     /**
35279      * Collapse all child nodes
35280      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35281      */
35282     collapseChildNodes : function(deep){
35283         var cs = this.childNodes;
35284         for(var i = 0, len = cs.length; i < len; i++) {
35285                 cs[i].collapse(deep);
35286         }
35287     },
35288
35289     /**
35290      * Disables this node
35291      */
35292     disable : function(){
35293         this.disabled = true;
35294         this.unselect();
35295         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35296             this.ui.onDisableChange(this, true);
35297         }
35298         this.fireEvent("disabledchange", this, true);
35299     },
35300
35301     /**
35302      * Enables this node
35303      */
35304     enable : function(){
35305         this.disabled = false;
35306         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35307             this.ui.onDisableChange(this, false);
35308         }
35309         this.fireEvent("disabledchange", this, false);
35310     },
35311
35312     // private
35313     renderChildren : function(suppressEvent){
35314         if(suppressEvent !== false){
35315             this.fireEvent("beforechildrenrendered", this);
35316         }
35317         var cs = this.childNodes;
35318         for(var i = 0, len = cs.length; i < len; i++){
35319             cs[i].render(true);
35320         }
35321         this.childrenRendered = true;
35322     },
35323
35324     // private
35325     sort : function(fn, scope){
35326         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35327         if(this.childrenRendered){
35328             var cs = this.childNodes;
35329             for(var i = 0, len = cs.length; i < len; i++){
35330                 cs[i].render(true);
35331             }
35332         }
35333     },
35334
35335     // private
35336     render : function(bulkRender){
35337         this.ui.render(bulkRender);
35338         if(!this.rendered){
35339             this.rendered = true;
35340             if(this.expanded){
35341                 this.expanded = false;
35342                 this.expand(false, false);
35343             }
35344         }
35345     },
35346
35347     // private
35348     renderIndent : function(deep, refresh){
35349         if(refresh){
35350             this.ui.childIndent = null;
35351         }
35352         this.ui.renderIndent();
35353         if(deep === true && this.childrenRendered){
35354             var cs = this.childNodes;
35355             for(var i = 0, len = cs.length; i < len; i++){
35356                 cs[i].renderIndent(true, refresh);
35357             }
35358         }
35359     }
35360 });/*
35361  * Based on:
35362  * Ext JS Library 1.1.1
35363  * Copyright(c) 2006-2007, Ext JS, LLC.
35364  *
35365  * Originally Released Under LGPL - original licence link has changed is not relivant.
35366  *
35367  * Fork - LGPL
35368  * <script type="text/javascript">
35369  */
35370  
35371 /**
35372  * @class Roo.tree.AsyncTreeNode
35373  * @extends Roo.tree.TreeNode
35374  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35375  * @constructor
35376  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35377  */
35378  Roo.tree.AsyncTreeNode = function(config){
35379     this.loaded = false;
35380     this.loading = false;
35381     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35382     /**
35383     * @event beforeload
35384     * Fires before this node is loaded, return false to cancel
35385     * @param {Node} this This node
35386     */
35387     this.addEvents({'beforeload':true, 'load': true});
35388     /**
35389     * @event load
35390     * Fires when this node is loaded
35391     * @param {Node} this This node
35392     */
35393     /**
35394      * The loader used by this node (defaults to using the tree's defined loader)
35395      * @type TreeLoader
35396      * @property loader
35397      */
35398 };
35399 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35400     expand : function(deep, anim, callback){
35401         if(this.loading){ // if an async load is already running, waiting til it's done
35402             var timer;
35403             var f = function(){
35404                 if(!this.loading){ // done loading
35405                     clearInterval(timer);
35406                     this.expand(deep, anim, callback);
35407                 }
35408             }.createDelegate(this);
35409             timer = setInterval(f, 200);
35410             return;
35411         }
35412         if(!this.loaded){
35413             if(this.fireEvent("beforeload", this) === false){
35414                 return;
35415             }
35416             this.loading = true;
35417             this.ui.beforeLoad(this);
35418             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35419             if(loader){
35420                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35421                 return;
35422             }
35423         }
35424         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35425     },
35426     
35427     /**
35428      * Returns true if this node is currently loading
35429      * @return {Boolean}
35430      */
35431     isLoading : function(){
35432         return this.loading;  
35433     },
35434     
35435     loadComplete : function(deep, anim, callback){
35436         this.loading = false;
35437         this.loaded = true;
35438         this.ui.afterLoad(this);
35439         this.fireEvent("load", this);
35440         this.expand(deep, anim, callback);
35441     },
35442     
35443     /**
35444      * Returns true if this node has been loaded
35445      * @return {Boolean}
35446      */
35447     isLoaded : function(){
35448         return this.loaded;
35449     },
35450     
35451     hasChildNodes : function(){
35452         if(!this.isLeaf() && !this.loaded){
35453             return true;
35454         }else{
35455             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35456         }
35457     },
35458
35459     /**
35460      * Trigger a reload for this node
35461      * @param {Function} callback
35462      */
35463     reload : function(callback){
35464         this.collapse(false, false);
35465         while(this.firstChild){
35466             this.removeChild(this.firstChild);
35467         }
35468         this.childrenRendered = false;
35469         this.loaded = false;
35470         if(this.isHiddenRoot()){
35471             this.expanded = false;
35472         }
35473         this.expand(false, false, callback);
35474     }
35475 });/*
35476  * Based on:
35477  * Ext JS Library 1.1.1
35478  * Copyright(c) 2006-2007, Ext JS, LLC.
35479  *
35480  * Originally Released Under LGPL - original licence link has changed is not relivant.
35481  *
35482  * Fork - LGPL
35483  * <script type="text/javascript">
35484  */
35485  
35486 /**
35487  * @class Roo.tree.TreeNodeUI
35488  * @constructor
35489  * @param {Object} node The node to render
35490  * The TreeNode UI implementation is separate from the
35491  * tree implementation. Unless you are customizing the tree UI,
35492  * you should never have to use this directly.
35493  */
35494 Roo.tree.TreeNodeUI = function(node){
35495     this.node = node;
35496     this.rendered = false;
35497     this.animating = false;
35498     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35499 };
35500
35501 Roo.tree.TreeNodeUI.prototype = {
35502     removeChild : function(node){
35503         if(this.rendered){
35504             this.ctNode.removeChild(node.ui.getEl());
35505         }
35506     },
35507
35508     beforeLoad : function(){
35509          this.addClass("x-tree-node-loading");
35510     },
35511
35512     afterLoad : function(){
35513          this.removeClass("x-tree-node-loading");
35514     },
35515
35516     onTextChange : function(node, text, oldText){
35517         if(this.rendered){
35518             this.textNode.innerHTML = text;
35519         }
35520     },
35521
35522     onDisableChange : function(node, state){
35523         this.disabled = state;
35524         if(state){
35525             this.addClass("x-tree-node-disabled");
35526         }else{
35527             this.removeClass("x-tree-node-disabled");
35528         }
35529     },
35530
35531     onSelectedChange : function(state){
35532         if(state){
35533             this.focus();
35534             this.addClass("x-tree-selected");
35535         }else{
35536             //this.blur();
35537             this.removeClass("x-tree-selected");
35538         }
35539     },
35540
35541     onMove : function(tree, node, oldParent, newParent, index, refNode){
35542         this.childIndent = null;
35543         if(this.rendered){
35544             var targetNode = newParent.ui.getContainer();
35545             if(!targetNode){//target not rendered
35546                 this.holder = document.createElement("div");
35547                 this.holder.appendChild(this.wrap);
35548                 return;
35549             }
35550             var insertBefore = refNode ? refNode.ui.getEl() : null;
35551             if(insertBefore){
35552                 targetNode.insertBefore(this.wrap, insertBefore);
35553             }else{
35554                 targetNode.appendChild(this.wrap);
35555             }
35556             this.node.renderIndent(true);
35557         }
35558     },
35559
35560     addClass : function(cls){
35561         if(this.elNode){
35562             Roo.fly(this.elNode).addClass(cls);
35563         }
35564     },
35565
35566     removeClass : function(cls){
35567         if(this.elNode){
35568             Roo.fly(this.elNode).removeClass(cls);
35569         }
35570     },
35571
35572     remove : function(){
35573         if(this.rendered){
35574             this.holder = document.createElement("div");
35575             this.holder.appendChild(this.wrap);
35576         }
35577     },
35578
35579     fireEvent : function(){
35580         return this.node.fireEvent.apply(this.node, arguments);
35581     },
35582
35583     initEvents : function(){
35584         this.node.on("move", this.onMove, this);
35585         var E = Roo.EventManager;
35586         var a = this.anchor;
35587
35588         var el = Roo.fly(a, '_treeui');
35589
35590         if(Roo.isOpera){ // opera render bug ignores the CSS
35591             el.setStyle("text-decoration", "none");
35592         }
35593
35594         el.on("click", this.onClick, this);
35595         el.on("dblclick", this.onDblClick, this);
35596
35597         if(this.checkbox){
35598             Roo.EventManager.on(this.checkbox,
35599                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35600         }
35601
35602         el.on("contextmenu", this.onContextMenu, this);
35603
35604         var icon = Roo.fly(this.iconNode);
35605         icon.on("click", this.onClick, this);
35606         icon.on("dblclick", this.onDblClick, this);
35607         icon.on("contextmenu", this.onContextMenu, this);
35608         E.on(this.ecNode, "click", this.ecClick, this, true);
35609
35610         if(this.node.disabled){
35611             this.addClass("x-tree-node-disabled");
35612         }
35613         if(this.node.hidden){
35614             this.addClass("x-tree-node-disabled");
35615         }
35616         var ot = this.node.getOwnerTree();
35617         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35618         if(dd && (!this.node.isRoot || ot.rootVisible)){
35619             Roo.dd.Registry.register(this.elNode, {
35620                 node: this.node,
35621                 handles: this.getDDHandles(),
35622                 isHandle: false
35623             });
35624         }
35625     },
35626
35627     getDDHandles : function(){
35628         return [this.iconNode, this.textNode];
35629     },
35630
35631     hide : function(){
35632         if(this.rendered){
35633             this.wrap.style.display = "none";
35634         }
35635     },
35636
35637     show : function(){
35638         if(this.rendered){
35639             this.wrap.style.display = "";
35640         }
35641     },
35642
35643     onContextMenu : function(e){
35644         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35645             e.preventDefault();
35646             this.focus();
35647             this.fireEvent("contextmenu", this.node, e);
35648         }
35649     },
35650
35651     onClick : function(e){
35652         if(this.dropping){
35653             e.stopEvent();
35654             return;
35655         }
35656         if(this.fireEvent("beforeclick", this.node, e) !== false){
35657             if(!this.disabled && this.node.attributes.href){
35658                 this.fireEvent("click", this.node, e);
35659                 return;
35660             }
35661             e.preventDefault();
35662             if(this.disabled){
35663                 return;
35664             }
35665
35666             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35667                 this.node.toggle();
35668             }
35669
35670             this.fireEvent("click", this.node, e);
35671         }else{
35672             e.stopEvent();
35673         }
35674     },
35675
35676     onDblClick : function(e){
35677         e.preventDefault();
35678         if(this.disabled){
35679             return;
35680         }
35681         if(this.checkbox){
35682             this.toggleCheck();
35683         }
35684         if(!this.animating && this.node.hasChildNodes()){
35685             this.node.toggle();
35686         }
35687         this.fireEvent("dblclick", this.node, e);
35688     },
35689
35690     onCheckChange : function(){
35691         var checked = this.checkbox.checked;
35692         this.node.attributes.checked = checked;
35693         this.fireEvent('checkchange', this.node, checked);
35694     },
35695
35696     ecClick : function(e){
35697         if(!this.animating && this.node.hasChildNodes()){
35698             this.node.toggle();
35699         }
35700     },
35701
35702     startDrop : function(){
35703         this.dropping = true;
35704     },
35705
35706     // delayed drop so the click event doesn't get fired on a drop
35707     endDrop : function(){
35708        setTimeout(function(){
35709            this.dropping = false;
35710        }.createDelegate(this), 50);
35711     },
35712
35713     expand : function(){
35714         this.updateExpandIcon();
35715         this.ctNode.style.display = "";
35716     },
35717
35718     focus : function(){
35719         if(!this.node.preventHScroll){
35720             try{this.anchor.focus();
35721             }catch(e){}
35722         }else if(!Roo.isIE){
35723             try{
35724                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35725                 var l = noscroll.scrollLeft;
35726                 this.anchor.focus();
35727                 noscroll.scrollLeft = l;
35728             }catch(e){}
35729         }
35730     },
35731
35732     toggleCheck : function(value){
35733         var cb = this.checkbox;
35734         if(cb){
35735             cb.checked = (value === undefined ? !cb.checked : value);
35736         }
35737     },
35738
35739     blur : function(){
35740         try{
35741             this.anchor.blur();
35742         }catch(e){}
35743     },
35744
35745     animExpand : function(callback){
35746         var ct = Roo.get(this.ctNode);
35747         ct.stopFx();
35748         if(!this.node.hasChildNodes()){
35749             this.updateExpandIcon();
35750             this.ctNode.style.display = "";
35751             Roo.callback(callback);
35752             return;
35753         }
35754         this.animating = true;
35755         this.updateExpandIcon();
35756
35757         ct.slideIn('t', {
35758            callback : function(){
35759                this.animating = false;
35760                Roo.callback(callback);
35761             },
35762             scope: this,
35763             duration: this.node.ownerTree.duration || .25
35764         });
35765     },
35766
35767     highlight : function(){
35768         var tree = this.node.getOwnerTree();
35769         Roo.fly(this.wrap).highlight(
35770             tree.hlColor || "C3DAF9",
35771             {endColor: tree.hlBaseColor}
35772         );
35773     },
35774
35775     collapse : function(){
35776         this.updateExpandIcon();
35777         this.ctNode.style.display = "none";
35778     },
35779
35780     animCollapse : function(callback){
35781         var ct = Roo.get(this.ctNode);
35782         ct.enableDisplayMode('block');
35783         ct.stopFx();
35784
35785         this.animating = true;
35786         this.updateExpandIcon();
35787
35788         ct.slideOut('t', {
35789             callback : function(){
35790                this.animating = false;
35791                Roo.callback(callback);
35792             },
35793             scope: this,
35794             duration: this.node.ownerTree.duration || .25
35795         });
35796     },
35797
35798     getContainer : function(){
35799         return this.ctNode;
35800     },
35801
35802     getEl : function(){
35803         return this.wrap;
35804     },
35805
35806     appendDDGhost : function(ghostNode){
35807         ghostNode.appendChild(this.elNode.cloneNode(true));
35808     },
35809
35810     getDDRepairXY : function(){
35811         return Roo.lib.Dom.getXY(this.iconNode);
35812     },
35813
35814     onRender : function(){
35815         this.render();
35816     },
35817
35818     render : function(bulkRender){
35819         var n = this.node, a = n.attributes;
35820         var targetNode = n.parentNode ?
35821               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
35822
35823         if(!this.rendered){
35824             this.rendered = true;
35825
35826             this.renderElements(n, a, targetNode, bulkRender);
35827
35828             if(a.qtip){
35829                if(this.textNode.setAttributeNS){
35830                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
35831                    if(a.qtipTitle){
35832                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
35833                    }
35834                }else{
35835                    this.textNode.setAttribute("ext:qtip", a.qtip);
35836                    if(a.qtipTitle){
35837                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
35838                    }
35839                }
35840             }else if(a.qtipCfg){
35841                 a.qtipCfg.target = Roo.id(this.textNode);
35842                 Roo.QuickTips.register(a.qtipCfg);
35843             }
35844             this.initEvents();
35845             if(!this.node.expanded){
35846                 this.updateExpandIcon();
35847             }
35848         }else{
35849             if(bulkRender === true) {
35850                 targetNode.appendChild(this.wrap);
35851             }
35852         }
35853     },
35854
35855     renderElements : function(n, a, targetNode, bulkRender)
35856     {
35857         // add some indent caching, this helps performance when rendering a large tree
35858         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35859         var t = n.getOwnerTree();
35860         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
35861         if (typeof(n.attributes.html) != 'undefined') {
35862             txt = n.attributes.html;
35863         }
35864         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
35865         var cb = typeof a.checked == 'boolean';
35866         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35867         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
35868             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
35869             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
35870             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
35871             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
35872             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
35873              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
35874                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
35875             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35876             "</li>"];
35877
35878         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35879             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35880                                 n.nextSibling.ui.getEl(), buf.join(""));
35881         }else{
35882             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35883         }
35884
35885         this.elNode = this.wrap.childNodes[0];
35886         this.ctNode = this.wrap.childNodes[1];
35887         var cs = this.elNode.childNodes;
35888         this.indentNode = cs[0];
35889         this.ecNode = cs[1];
35890         this.iconNode = cs[2];
35891         var index = 3;
35892         if(cb){
35893             this.checkbox = cs[3];
35894             index++;
35895         }
35896         this.anchor = cs[index];
35897         this.textNode = cs[index].firstChild;
35898     },
35899
35900     getAnchor : function(){
35901         return this.anchor;
35902     },
35903
35904     getTextEl : function(){
35905         return this.textNode;
35906     },
35907
35908     getIconEl : function(){
35909         return this.iconNode;
35910     },
35911
35912     isChecked : function(){
35913         return this.checkbox ? this.checkbox.checked : false;
35914     },
35915
35916     updateExpandIcon : function(){
35917         if(this.rendered){
35918             var n = this.node, c1, c2;
35919             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
35920             var hasChild = n.hasChildNodes();
35921             if(hasChild){
35922                 if(n.expanded){
35923                     cls += "-minus";
35924                     c1 = "x-tree-node-collapsed";
35925                     c2 = "x-tree-node-expanded";
35926                 }else{
35927                     cls += "-plus";
35928                     c1 = "x-tree-node-expanded";
35929                     c2 = "x-tree-node-collapsed";
35930                 }
35931                 if(this.wasLeaf){
35932                     this.removeClass("x-tree-node-leaf");
35933                     this.wasLeaf = false;
35934                 }
35935                 if(this.c1 != c1 || this.c2 != c2){
35936                     Roo.fly(this.elNode).replaceClass(c1, c2);
35937                     this.c1 = c1; this.c2 = c2;
35938                 }
35939             }else{
35940                 // this changes non-leafs into leafs if they have no children.
35941                 // it's not very rational behaviour..
35942                 
35943                 if(!this.wasLeaf && this.node.leaf){
35944                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
35945                     delete this.c1;
35946                     delete this.c2;
35947                     this.wasLeaf = true;
35948                 }
35949             }
35950             var ecc = "x-tree-ec-icon "+cls;
35951             if(this.ecc != ecc){
35952                 this.ecNode.className = ecc;
35953                 this.ecc = ecc;
35954             }
35955         }
35956     },
35957
35958     getChildIndent : function(){
35959         if(!this.childIndent){
35960             var buf = [];
35961             var p = this.node;
35962             while(p){
35963                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
35964                     if(!p.isLast()) {
35965                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
35966                     } else {
35967                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
35968                     }
35969                 }
35970                 p = p.parentNode;
35971             }
35972             this.childIndent = buf.join("");
35973         }
35974         return this.childIndent;
35975     },
35976
35977     renderIndent : function(){
35978         if(this.rendered){
35979             var indent = "";
35980             var p = this.node.parentNode;
35981             if(p){
35982                 indent = p.ui.getChildIndent();
35983             }
35984             if(this.indentMarkup != indent){ // don't rerender if not required
35985                 this.indentNode.innerHTML = indent;
35986                 this.indentMarkup = indent;
35987             }
35988             this.updateExpandIcon();
35989         }
35990     }
35991 };
35992
35993 Roo.tree.RootTreeNodeUI = function(){
35994     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
35995 };
35996 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
35997     render : function(){
35998         if(!this.rendered){
35999             var targetNode = this.node.ownerTree.innerCt.dom;
36000             this.node.expanded = true;
36001             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36002             this.wrap = this.ctNode = targetNode.firstChild;
36003         }
36004     },
36005     collapse : function(){
36006     },
36007     expand : function(){
36008     }
36009 });/*
36010  * Based on:
36011  * Ext JS Library 1.1.1
36012  * Copyright(c) 2006-2007, Ext JS, LLC.
36013  *
36014  * Originally Released Under LGPL - original licence link has changed is not relivant.
36015  *
36016  * Fork - LGPL
36017  * <script type="text/javascript">
36018  */
36019 /**
36020  * @class Roo.tree.TreeLoader
36021  * @extends Roo.util.Observable
36022  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36023  * nodes from a specified URL. The response must be a javascript Array definition
36024  * who's elements are node definition objects. eg:
36025  * <pre><code>
36026 {  success : true,
36027    data :      [
36028    
36029     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36030     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36031     ]
36032 }
36033
36034
36035 </code></pre>
36036  * <br><br>
36037  * The old style respose with just an array is still supported, but not recommended.
36038  * <br><br>
36039  *
36040  * A server request is sent, and child nodes are loaded only when a node is expanded.
36041  * The loading node's id is passed to the server under the parameter name "node" to
36042  * enable the server to produce the correct child nodes.
36043  * <br><br>
36044  * To pass extra parameters, an event handler may be attached to the "beforeload"
36045  * event, and the parameters specified in the TreeLoader's baseParams property:
36046  * <pre><code>
36047     myTreeLoader.on("beforeload", function(treeLoader, node) {
36048         this.baseParams.category = node.attributes.category;
36049     }, this);
36050     
36051 </code></pre>
36052  *
36053  * This would pass an HTTP parameter called "category" to the server containing
36054  * the value of the Node's "category" attribute.
36055  * @constructor
36056  * Creates a new Treeloader.
36057  * @param {Object} config A config object containing config properties.
36058  */
36059 Roo.tree.TreeLoader = function(config){
36060     this.baseParams = {};
36061     this.requestMethod = "POST";
36062     Roo.apply(this, config);
36063
36064     this.addEvents({
36065     
36066         /**
36067          * @event beforeload
36068          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36069          * @param {Object} This TreeLoader object.
36070          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36071          * @param {Object} callback The callback function specified in the {@link #load} call.
36072          */
36073         beforeload : true,
36074         /**
36075          * @event load
36076          * Fires when the node has been successfuly loaded.
36077          * @param {Object} This TreeLoader object.
36078          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36079          * @param {Object} response The response object containing the data from the server.
36080          */
36081         load : true,
36082         /**
36083          * @event loadexception
36084          * Fires if the network request failed.
36085          * @param {Object} This TreeLoader object.
36086          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36087          * @param {Object} response The response object containing the data from the server.
36088          */
36089         loadexception : true,
36090         /**
36091          * @event create
36092          * Fires before a node is created, enabling you to return custom Node types 
36093          * @param {Object} This TreeLoader object.
36094          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36095          */
36096         create : true
36097     });
36098
36099     Roo.tree.TreeLoader.superclass.constructor.call(this);
36100 };
36101
36102 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36103     /**
36104     * @cfg {String} dataUrl The URL from which to request a Json string which
36105     * specifies an array of node definition object representing the child nodes
36106     * to be loaded.
36107     */
36108     /**
36109     * @cfg {String} requestMethod either GET or POST
36110     * defaults to POST (due to BC)
36111     * to be loaded.
36112     */
36113     /**
36114     * @cfg {Object} baseParams (optional) An object containing properties which
36115     * specify HTTP parameters to be passed to each request for child nodes.
36116     */
36117     /**
36118     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36119     * created by this loader. If the attributes sent by the server have an attribute in this object,
36120     * they take priority.
36121     */
36122     /**
36123     * @cfg {Object} uiProviders (optional) An object containing properties which
36124     * 
36125     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36126     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36127     * <i>uiProvider</i> attribute of a returned child node is a string rather
36128     * than a reference to a TreeNodeUI implementation, this that string value
36129     * is used as a property name in the uiProviders object. You can define the provider named
36130     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36131     */
36132     uiProviders : {},
36133
36134     /**
36135     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36136     * child nodes before loading.
36137     */
36138     clearOnLoad : true,
36139
36140     /**
36141     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36142     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36143     * Grid query { data : [ .....] }
36144     */
36145     
36146     root : false,
36147      /**
36148     * @cfg {String} queryParam (optional) 
36149     * Name of the query as it will be passed on the querystring (defaults to 'node')
36150     * eg. the request will be ?node=[id]
36151     */
36152     
36153     
36154     queryParam: false,
36155     
36156     /**
36157      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36158      * This is called automatically when a node is expanded, but may be used to reload
36159      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36160      * @param {Roo.tree.TreeNode} node
36161      * @param {Function} callback
36162      */
36163     load : function(node, callback){
36164         if(this.clearOnLoad){
36165             while(node.firstChild){
36166                 node.removeChild(node.firstChild);
36167             }
36168         }
36169         if(node.attributes.children){ // preloaded json children
36170             var cs = node.attributes.children;
36171             for(var i = 0, len = cs.length; i < len; i++){
36172                 node.appendChild(this.createNode(cs[i]));
36173             }
36174             if(typeof callback == "function"){
36175                 callback();
36176             }
36177         }else if(this.dataUrl){
36178             this.requestData(node, callback);
36179         }
36180     },
36181
36182     getParams: function(node){
36183         var buf = [], bp = this.baseParams;
36184         for(var key in bp){
36185             if(typeof bp[key] != "function"){
36186                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36187             }
36188         }
36189         var n = this.queryParam === false ? 'node' : this.queryParam;
36190         buf.push(n + "=", encodeURIComponent(node.id));
36191         return buf.join("");
36192     },
36193
36194     requestData : function(node, callback){
36195         if(this.fireEvent("beforeload", this, node, callback) !== false){
36196             this.transId = Roo.Ajax.request({
36197                 method:this.requestMethod,
36198                 url: this.dataUrl||this.url,
36199                 success: this.handleResponse,
36200                 failure: this.handleFailure,
36201                 scope: this,
36202                 argument: {callback: callback, node: node},
36203                 params: this.getParams(node)
36204             });
36205         }else{
36206             // if the load is cancelled, make sure we notify
36207             // the node that we are done
36208             if(typeof callback == "function"){
36209                 callback();
36210             }
36211         }
36212     },
36213
36214     isLoading : function(){
36215         return this.transId ? true : false;
36216     },
36217
36218     abort : function(){
36219         if(this.isLoading()){
36220             Roo.Ajax.abort(this.transId);
36221         }
36222     },
36223
36224     // private
36225     createNode : function(attr)
36226     {
36227         // apply baseAttrs, nice idea Corey!
36228         if(this.baseAttrs){
36229             Roo.applyIf(attr, this.baseAttrs);
36230         }
36231         if(this.applyLoader !== false){
36232             attr.loader = this;
36233         }
36234         // uiProvider = depreciated..
36235         
36236         if(typeof(attr.uiProvider) == 'string'){
36237            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36238                 /**  eval:var:attr */ eval(attr.uiProvider);
36239         }
36240         if(typeof(this.uiProviders['default']) != 'undefined') {
36241             attr.uiProvider = this.uiProviders['default'];
36242         }
36243         
36244         this.fireEvent('create', this, attr);
36245         
36246         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36247         return(attr.leaf ?
36248                         new Roo.tree.TreeNode(attr) :
36249                         new Roo.tree.AsyncTreeNode(attr));
36250     },
36251
36252     processResponse : function(response, node, callback)
36253     {
36254         var json = response.responseText;
36255         try {
36256             
36257             var o = Roo.decode(json);
36258             
36259             if (this.root === false && typeof(o.success) != undefined) {
36260                 this.root = 'data'; // the default behaviour for list like data..
36261                 }
36262                 
36263             if (this.root !== false &&  !o.success) {
36264                 // it's a failure condition.
36265                 var a = response.argument;
36266                 this.fireEvent("loadexception", this, a.node, response);
36267                 Roo.log("Load failed - should have a handler really");
36268                 return;
36269             }
36270             
36271             
36272             
36273             if (this.root !== false) {
36274                  o = o[this.root];
36275             }
36276             
36277             for(var i = 0, len = o.length; i < len; i++){
36278                 var n = this.createNode(o[i]);
36279                 if(n){
36280                     node.appendChild(n);
36281                 }
36282             }
36283             if(typeof callback == "function"){
36284                 callback(this, node);
36285             }
36286         }catch(e){
36287             this.handleFailure(response);
36288         }
36289     },
36290
36291     handleResponse : function(response){
36292         this.transId = false;
36293         var a = response.argument;
36294         this.processResponse(response, a.node, a.callback);
36295         this.fireEvent("load", this, a.node, response);
36296     },
36297
36298     handleFailure : function(response)
36299     {
36300         // should handle failure better..
36301         this.transId = false;
36302         var a = response.argument;
36303         this.fireEvent("loadexception", this, a.node, response);
36304         if(typeof a.callback == "function"){
36305             a.callback(this, a.node);
36306         }
36307     }
36308 });/*
36309  * Based on:
36310  * Ext JS Library 1.1.1
36311  * Copyright(c) 2006-2007, Ext JS, LLC.
36312  *
36313  * Originally Released Under LGPL - original licence link has changed is not relivant.
36314  *
36315  * Fork - LGPL
36316  * <script type="text/javascript">
36317  */
36318
36319 /**
36320 * @class Roo.tree.TreeFilter
36321 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36322 * @param {TreePanel} tree
36323 * @param {Object} config (optional)
36324  */
36325 Roo.tree.TreeFilter = function(tree, config){
36326     this.tree = tree;
36327     this.filtered = {};
36328     Roo.apply(this, config);
36329 };
36330
36331 Roo.tree.TreeFilter.prototype = {
36332     clearBlank:false,
36333     reverse:false,
36334     autoClear:false,
36335     remove:false,
36336
36337      /**
36338      * Filter the data by a specific attribute.
36339      * @param {String/RegExp} value Either string that the attribute value
36340      * should start with or a RegExp to test against the attribute
36341      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36342      * @param {TreeNode} startNode (optional) The node to start the filter at.
36343      */
36344     filter : function(value, attr, startNode){
36345         attr = attr || "text";
36346         var f;
36347         if(typeof value == "string"){
36348             var vlen = value.length;
36349             // auto clear empty filter
36350             if(vlen == 0 && this.clearBlank){
36351                 this.clear();
36352                 return;
36353             }
36354             value = value.toLowerCase();
36355             f = function(n){
36356                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36357             };
36358         }else if(value.exec){ // regex?
36359             f = function(n){
36360                 return value.test(n.attributes[attr]);
36361             };
36362         }else{
36363             throw 'Illegal filter type, must be string or regex';
36364         }
36365         this.filterBy(f, null, startNode);
36366         },
36367
36368     /**
36369      * Filter by a function. The passed function will be called with each
36370      * node in the tree (or from the startNode). If the function returns true, the node is kept
36371      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36372      * @param {Function} fn The filter function
36373      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36374      */
36375     filterBy : function(fn, scope, startNode){
36376         startNode = startNode || this.tree.root;
36377         if(this.autoClear){
36378             this.clear();
36379         }
36380         var af = this.filtered, rv = this.reverse;
36381         var f = function(n){
36382             if(n == startNode){
36383                 return true;
36384             }
36385             if(af[n.id]){
36386                 return false;
36387             }
36388             var m = fn.call(scope || n, n);
36389             if(!m || rv){
36390                 af[n.id] = n;
36391                 n.ui.hide();
36392                 return false;
36393             }
36394             return true;
36395         };
36396         startNode.cascade(f);
36397         if(this.remove){
36398            for(var id in af){
36399                if(typeof id != "function"){
36400                    var n = af[id];
36401                    if(n && n.parentNode){
36402                        n.parentNode.removeChild(n);
36403                    }
36404                }
36405            }
36406         }
36407     },
36408
36409     /**
36410      * Clears the current filter. Note: with the "remove" option
36411      * set a filter cannot be cleared.
36412      */
36413     clear : function(){
36414         var t = this.tree;
36415         var af = this.filtered;
36416         for(var id in af){
36417             if(typeof id != "function"){
36418                 var n = af[id];
36419                 if(n){
36420                     n.ui.show();
36421                 }
36422             }
36423         }
36424         this.filtered = {};
36425     }
36426 };
36427 /*
36428  * Based on:
36429  * Ext JS Library 1.1.1
36430  * Copyright(c) 2006-2007, Ext JS, LLC.
36431  *
36432  * Originally Released Under LGPL - original licence link has changed is not relivant.
36433  *
36434  * Fork - LGPL
36435  * <script type="text/javascript">
36436  */
36437  
36438
36439 /**
36440  * @class Roo.tree.TreeSorter
36441  * Provides sorting of nodes in a TreePanel
36442  * 
36443  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36444  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36445  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36446  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36447  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36448  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36449  * @constructor
36450  * @param {TreePanel} tree
36451  * @param {Object} config
36452  */
36453 Roo.tree.TreeSorter = function(tree, config){
36454     Roo.apply(this, config);
36455     tree.on("beforechildrenrendered", this.doSort, this);
36456     tree.on("append", this.updateSort, this);
36457     tree.on("insert", this.updateSort, this);
36458     
36459     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36460     var p = this.property || "text";
36461     var sortType = this.sortType;
36462     var fs = this.folderSort;
36463     var cs = this.caseSensitive === true;
36464     var leafAttr = this.leafAttr || 'leaf';
36465
36466     this.sortFn = function(n1, n2){
36467         if(fs){
36468             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36469                 return 1;
36470             }
36471             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36472                 return -1;
36473             }
36474         }
36475         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36476         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36477         if(v1 < v2){
36478                         return dsc ? +1 : -1;
36479                 }else if(v1 > v2){
36480                         return dsc ? -1 : +1;
36481         }else{
36482                 return 0;
36483         }
36484     };
36485 };
36486
36487 Roo.tree.TreeSorter.prototype = {
36488     doSort : function(node){
36489         node.sort(this.sortFn);
36490     },
36491     
36492     compareNodes : function(n1, n2){
36493         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36494     },
36495     
36496     updateSort : function(tree, node){
36497         if(node.childrenRendered){
36498             this.doSort.defer(1, this, [node]);
36499         }
36500     }
36501 };/*
36502  * Based on:
36503  * Ext JS Library 1.1.1
36504  * Copyright(c) 2006-2007, Ext JS, LLC.
36505  *
36506  * Originally Released Under LGPL - original licence link has changed is not relivant.
36507  *
36508  * Fork - LGPL
36509  * <script type="text/javascript">
36510  */
36511
36512 if(Roo.dd.DropZone){
36513     
36514 Roo.tree.TreeDropZone = function(tree, config){
36515     this.allowParentInsert = false;
36516     this.allowContainerDrop = false;
36517     this.appendOnly = false;
36518     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36519     this.tree = tree;
36520     this.lastInsertClass = "x-tree-no-status";
36521     this.dragOverData = {};
36522 };
36523
36524 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36525     ddGroup : "TreeDD",
36526     scroll:  true,
36527     
36528     expandDelay : 1000,
36529     
36530     expandNode : function(node){
36531         if(node.hasChildNodes() && !node.isExpanded()){
36532             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36533         }
36534     },
36535     
36536     queueExpand : function(node){
36537         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36538     },
36539     
36540     cancelExpand : function(){
36541         if(this.expandProcId){
36542             clearTimeout(this.expandProcId);
36543             this.expandProcId = false;
36544         }
36545     },
36546     
36547     isValidDropPoint : function(n, pt, dd, e, data){
36548         if(!n || !data){ return false; }
36549         var targetNode = n.node;
36550         var dropNode = data.node;
36551         // default drop rules
36552         if(!(targetNode && targetNode.isTarget && pt)){
36553             return false;
36554         }
36555         if(pt == "append" && targetNode.allowChildren === false){
36556             return false;
36557         }
36558         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36559             return false;
36560         }
36561         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36562             return false;
36563         }
36564         // reuse the object
36565         var overEvent = this.dragOverData;
36566         overEvent.tree = this.tree;
36567         overEvent.target = targetNode;
36568         overEvent.data = data;
36569         overEvent.point = pt;
36570         overEvent.source = dd;
36571         overEvent.rawEvent = e;
36572         overEvent.dropNode = dropNode;
36573         overEvent.cancel = false;  
36574         var result = this.tree.fireEvent("nodedragover", overEvent);
36575         return overEvent.cancel === false && result !== false;
36576     },
36577     
36578     getDropPoint : function(e, n, dd)
36579     {
36580         var tn = n.node;
36581         if(tn.isRoot){
36582             return tn.allowChildren !== false ? "append" : false; // always append for root
36583         }
36584         var dragEl = n.ddel;
36585         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36586         var y = Roo.lib.Event.getPageY(e);
36587         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36588         
36589         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36590         var noAppend = tn.allowChildren === false;
36591         if(this.appendOnly || tn.parentNode.allowChildren === false){
36592             return noAppend ? false : "append";
36593         }
36594         var noBelow = false;
36595         if(!this.allowParentInsert){
36596             noBelow = tn.hasChildNodes() && tn.isExpanded();
36597         }
36598         var q = (b - t) / (noAppend ? 2 : 3);
36599         if(y >= t && y < (t + q)){
36600             return "above";
36601         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36602             return "below";
36603         }else{
36604             return "append";
36605         }
36606     },
36607     
36608     onNodeEnter : function(n, dd, e, data)
36609     {
36610         this.cancelExpand();
36611     },
36612     
36613     onNodeOver : function(n, dd, e, data)
36614     {
36615        
36616         var pt = this.getDropPoint(e, n, dd);
36617         var node = n.node;
36618         
36619         // auto node expand check
36620         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36621             this.queueExpand(node);
36622         }else if(pt != "append"){
36623             this.cancelExpand();
36624         }
36625         
36626         // set the insert point style on the target node
36627         var returnCls = this.dropNotAllowed;
36628         if(this.isValidDropPoint(n, pt, dd, e, data)){
36629            if(pt){
36630                var el = n.ddel;
36631                var cls;
36632                if(pt == "above"){
36633                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36634                    cls = "x-tree-drag-insert-above";
36635                }else if(pt == "below"){
36636                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36637                    cls = "x-tree-drag-insert-below";
36638                }else{
36639                    returnCls = "x-tree-drop-ok-append";
36640                    cls = "x-tree-drag-append";
36641                }
36642                if(this.lastInsertClass != cls){
36643                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36644                    this.lastInsertClass = cls;
36645                }
36646            }
36647        }
36648        return returnCls;
36649     },
36650     
36651     onNodeOut : function(n, dd, e, data){
36652         
36653         this.cancelExpand();
36654         this.removeDropIndicators(n);
36655     },
36656     
36657     onNodeDrop : function(n, dd, e, data){
36658         var point = this.getDropPoint(e, n, dd);
36659         var targetNode = n.node;
36660         targetNode.ui.startDrop();
36661         if(!this.isValidDropPoint(n, point, dd, e, data)){
36662             targetNode.ui.endDrop();
36663             return false;
36664         }
36665         // first try to find the drop node
36666         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36667         var dropEvent = {
36668             tree : this.tree,
36669             target: targetNode,
36670             data: data,
36671             point: point,
36672             source: dd,
36673             rawEvent: e,
36674             dropNode: dropNode,
36675             cancel: !dropNode   
36676         };
36677         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36678         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36679             targetNode.ui.endDrop();
36680             return false;
36681         }
36682         // allow target changing
36683         targetNode = dropEvent.target;
36684         if(point == "append" && !targetNode.isExpanded()){
36685             targetNode.expand(false, null, function(){
36686                 this.completeDrop(dropEvent);
36687             }.createDelegate(this));
36688         }else{
36689             this.completeDrop(dropEvent);
36690         }
36691         return true;
36692     },
36693     
36694     completeDrop : function(de){
36695         var ns = de.dropNode, p = de.point, t = de.target;
36696         if(!(ns instanceof Array)){
36697             ns = [ns];
36698         }
36699         var n;
36700         for(var i = 0, len = ns.length; i < len; i++){
36701             n = ns[i];
36702             if(p == "above"){
36703                 t.parentNode.insertBefore(n, t);
36704             }else if(p == "below"){
36705                 t.parentNode.insertBefore(n, t.nextSibling);
36706             }else{
36707                 t.appendChild(n);
36708             }
36709         }
36710         n.ui.focus();
36711         if(this.tree.hlDrop){
36712             n.ui.highlight();
36713         }
36714         t.ui.endDrop();
36715         this.tree.fireEvent("nodedrop", de);
36716     },
36717     
36718     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36719         if(this.tree.hlDrop){
36720             dropNode.ui.focus();
36721             dropNode.ui.highlight();
36722         }
36723         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36724     },
36725     
36726     getTree : function(){
36727         return this.tree;
36728     },
36729     
36730     removeDropIndicators : function(n){
36731         if(n && n.ddel){
36732             var el = n.ddel;
36733             Roo.fly(el).removeClass([
36734                     "x-tree-drag-insert-above",
36735                     "x-tree-drag-insert-below",
36736                     "x-tree-drag-append"]);
36737             this.lastInsertClass = "_noclass";
36738         }
36739     },
36740     
36741     beforeDragDrop : function(target, e, id){
36742         this.cancelExpand();
36743         return true;
36744     },
36745     
36746     afterRepair : function(data){
36747         if(data && Roo.enableFx){
36748             data.node.ui.highlight();
36749         }
36750         this.hideProxy();
36751     } 
36752     
36753 });
36754
36755 }
36756 /*
36757  * Based on:
36758  * Ext JS Library 1.1.1
36759  * Copyright(c) 2006-2007, Ext JS, LLC.
36760  *
36761  * Originally Released Under LGPL - original licence link has changed is not relivant.
36762  *
36763  * Fork - LGPL
36764  * <script type="text/javascript">
36765  */
36766  
36767
36768 if(Roo.dd.DragZone){
36769 Roo.tree.TreeDragZone = function(tree, config){
36770     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36771     this.tree = tree;
36772 };
36773
36774 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36775     ddGroup : "TreeDD",
36776    
36777     onBeforeDrag : function(data, e){
36778         var n = data.node;
36779         return n && n.draggable && !n.disabled;
36780     },
36781      
36782     
36783     onInitDrag : function(e){
36784         var data = this.dragData;
36785         this.tree.getSelectionModel().select(data.node);
36786         this.proxy.update("");
36787         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
36788         this.tree.fireEvent("startdrag", this.tree, data.node, e);
36789     },
36790     
36791     getRepairXY : function(e, data){
36792         return data.node.ui.getDDRepairXY();
36793     },
36794     
36795     onEndDrag : function(data, e){
36796         this.tree.fireEvent("enddrag", this.tree, data.node, e);
36797         
36798         
36799     },
36800     
36801     onValidDrop : function(dd, e, id){
36802         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
36803         this.hideProxy();
36804     },
36805     
36806     beforeInvalidDrop : function(e, id){
36807         // this scrolls the original position back into view
36808         var sm = this.tree.getSelectionModel();
36809         sm.clearSelections();
36810         sm.select(this.dragData.node);
36811     }
36812 });
36813 }/*
36814  * Based on:
36815  * Ext JS Library 1.1.1
36816  * Copyright(c) 2006-2007, Ext JS, LLC.
36817  *
36818  * Originally Released Under LGPL - original licence link has changed is not relivant.
36819  *
36820  * Fork - LGPL
36821  * <script type="text/javascript">
36822  */
36823 /**
36824  * @class Roo.tree.TreeEditor
36825  * @extends Roo.Editor
36826  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
36827  * as the editor field.
36828  * @constructor
36829  * @param {Object} config (used to be the tree panel.)
36830  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
36831  * 
36832  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
36833  * @cfg {Roo.form.TextField|Object} field The field configuration
36834  *
36835  * 
36836  */
36837 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
36838     var tree = config;
36839     var field;
36840     if (oldconfig) { // old style..
36841         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
36842     } else {
36843         // new style..
36844         tree = config.tree;
36845         config.field = config.field  || {};
36846         config.field.xtype = 'TextField';
36847         field = Roo.factory(config.field, Roo.form);
36848     }
36849     config = config || {};
36850     
36851     
36852     this.addEvents({
36853         /**
36854          * @event beforenodeedit
36855          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
36856          * false from the handler of this event.
36857          * @param {Editor} this
36858          * @param {Roo.tree.Node} node 
36859          */
36860         "beforenodeedit" : true
36861     });
36862     
36863     //Roo.log(config);
36864     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
36865
36866     this.tree = tree;
36867
36868     tree.on('beforeclick', this.beforeNodeClick, this);
36869     tree.getTreeEl().on('mousedown', this.hide, this);
36870     this.on('complete', this.updateNode, this);
36871     this.on('beforestartedit', this.fitToTree, this);
36872     this.on('startedit', this.bindScroll, this, {delay:10});
36873     this.on('specialkey', this.onSpecialKey, this);
36874 };
36875
36876 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
36877     /**
36878      * @cfg {String} alignment
36879      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
36880      */
36881     alignment: "l-l",
36882     // inherit
36883     autoSize: false,
36884     /**
36885      * @cfg {Boolean} hideEl
36886      * True to hide the bound element while the editor is displayed (defaults to false)
36887      */
36888     hideEl : false,
36889     /**
36890      * @cfg {String} cls
36891      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
36892      */
36893     cls: "x-small-editor x-tree-editor",
36894     /**
36895      * @cfg {Boolean} shim
36896      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
36897      */
36898     shim:false,
36899     // inherit
36900     shadow:"frame",
36901     /**
36902      * @cfg {Number} maxWidth
36903      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
36904      * the containing tree element's size, it will be automatically limited for you to the container width, taking
36905      * scroll and client offsets into account prior to each edit.
36906      */
36907     maxWidth: 250,
36908
36909     editDelay : 350,
36910
36911     // private
36912     fitToTree : function(ed, el){
36913         var td = this.tree.getTreeEl().dom, nd = el.dom;
36914         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
36915             td.scrollLeft = nd.offsetLeft;
36916         }
36917         var w = Math.min(
36918                 this.maxWidth,
36919                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
36920         this.setSize(w, '');
36921         
36922         return this.fireEvent('beforenodeedit', this, this.editNode);
36923         
36924     },
36925
36926     // private
36927     triggerEdit : function(node){
36928         this.completeEdit();
36929         this.editNode = node;
36930         this.startEdit(node.ui.textNode, node.text);
36931     },
36932
36933     // private
36934     bindScroll : function(){
36935         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
36936     },
36937
36938     // private
36939     beforeNodeClick : function(node, e){
36940         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
36941         this.lastClick = new Date();
36942         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
36943             e.stopEvent();
36944             this.triggerEdit(node);
36945             return false;
36946         }
36947         return true;
36948     },
36949
36950     // private
36951     updateNode : function(ed, value){
36952         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
36953         this.editNode.setText(value);
36954     },
36955
36956     // private
36957     onHide : function(){
36958         Roo.tree.TreeEditor.superclass.onHide.call(this);
36959         if(this.editNode){
36960             this.editNode.ui.focus();
36961         }
36962     },
36963
36964     // private
36965     onSpecialKey : function(field, e){
36966         var k = e.getKey();
36967         if(k == e.ESC){
36968             e.stopEvent();
36969             this.cancelEdit();
36970         }else if(k == e.ENTER && !e.hasModifier()){
36971             e.stopEvent();
36972             this.completeEdit();
36973         }
36974     }
36975 });//<Script type="text/javascript">
36976 /*
36977  * Based on:
36978  * Ext JS Library 1.1.1
36979  * Copyright(c) 2006-2007, Ext JS, LLC.
36980  *
36981  * Originally Released Under LGPL - original licence link has changed is not relivant.
36982  *
36983  * Fork - LGPL
36984  * <script type="text/javascript">
36985  */
36986  
36987 /**
36988  * Not documented??? - probably should be...
36989  */
36990
36991 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
36992     //focus: Roo.emptyFn, // prevent odd scrolling behavior
36993     
36994     renderElements : function(n, a, targetNode, bulkRender){
36995         //consel.log("renderElements?");
36996         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36997
36998         var t = n.getOwnerTree();
36999         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37000         
37001         var cols = t.columns;
37002         var bw = t.borderWidth;
37003         var c = cols[0];
37004         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37005          var cb = typeof a.checked == "boolean";
37006         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37007         var colcls = 'x-t-' + tid + '-c0';
37008         var buf = [
37009             '<li class="x-tree-node">',
37010             
37011                 
37012                 '<div class="x-tree-node-el ', a.cls,'">',
37013                     // extran...
37014                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37015                 
37016                 
37017                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37018                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37019                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37020                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37021                            (a.iconCls ? ' '+a.iconCls : ''),
37022                            '" unselectable="on" />',
37023                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37024                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37025                              
37026                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37027                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37028                             '<span unselectable="on" qtip="' + tx + '">',
37029                              tx,
37030                              '</span></a>' ,
37031                     '</div>',
37032                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37033                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37034                  ];
37035         for(var i = 1, len = cols.length; i < len; i++){
37036             c = cols[i];
37037             colcls = 'x-t-' + tid + '-c' +i;
37038             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37039             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37040                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37041                       "</div>");
37042          }
37043          
37044          buf.push(
37045             '</a>',
37046             '<div class="x-clear"></div></div>',
37047             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37048             "</li>");
37049         
37050         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37051             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37052                                 n.nextSibling.ui.getEl(), buf.join(""));
37053         }else{
37054             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37055         }
37056         var el = this.wrap.firstChild;
37057         this.elRow = el;
37058         this.elNode = el.firstChild;
37059         this.ranchor = el.childNodes[1];
37060         this.ctNode = this.wrap.childNodes[1];
37061         var cs = el.firstChild.childNodes;
37062         this.indentNode = cs[0];
37063         this.ecNode = cs[1];
37064         this.iconNode = cs[2];
37065         var index = 3;
37066         if(cb){
37067             this.checkbox = cs[3];
37068             index++;
37069         }
37070         this.anchor = cs[index];
37071         
37072         this.textNode = cs[index].firstChild;
37073         
37074         //el.on("click", this.onClick, this);
37075         //el.on("dblclick", this.onDblClick, this);
37076         
37077         
37078        // console.log(this);
37079     },
37080     initEvents : function(){
37081         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37082         
37083             
37084         var a = this.ranchor;
37085
37086         var el = Roo.get(a);
37087
37088         if(Roo.isOpera){ // opera render bug ignores the CSS
37089             el.setStyle("text-decoration", "none");
37090         }
37091
37092         el.on("click", this.onClick, this);
37093         el.on("dblclick", this.onDblClick, this);
37094         el.on("contextmenu", this.onContextMenu, this);
37095         
37096     },
37097     
37098     /*onSelectedChange : function(state){
37099         if(state){
37100             this.focus();
37101             this.addClass("x-tree-selected");
37102         }else{
37103             //this.blur();
37104             this.removeClass("x-tree-selected");
37105         }
37106     },*/
37107     addClass : function(cls){
37108         if(this.elRow){
37109             Roo.fly(this.elRow).addClass(cls);
37110         }
37111         
37112     },
37113     
37114     
37115     removeClass : function(cls){
37116         if(this.elRow){
37117             Roo.fly(this.elRow).removeClass(cls);
37118         }
37119     }
37120
37121     
37122     
37123 });//<Script type="text/javascript">
37124
37125 /*
37126  * Based on:
37127  * Ext JS Library 1.1.1
37128  * Copyright(c) 2006-2007, Ext JS, LLC.
37129  *
37130  * Originally Released Under LGPL - original licence link has changed is not relivant.
37131  *
37132  * Fork - LGPL
37133  * <script type="text/javascript">
37134  */
37135  
37136
37137 /**
37138  * @class Roo.tree.ColumnTree
37139  * @extends Roo.data.TreePanel
37140  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37141  * @cfg {int} borderWidth  compined right/left border allowance
37142  * @constructor
37143  * @param {String/HTMLElement/Element} el The container element
37144  * @param {Object} config
37145  */
37146 Roo.tree.ColumnTree =  function(el, config)
37147 {
37148    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37149    this.addEvents({
37150         /**
37151         * @event resize
37152         * Fire this event on a container when it resizes
37153         * @param {int} w Width
37154         * @param {int} h Height
37155         */
37156        "resize" : true
37157     });
37158     this.on('resize', this.onResize, this);
37159 };
37160
37161 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37162     //lines:false,
37163     
37164     
37165     borderWidth: Roo.isBorderBox ? 0 : 2, 
37166     headEls : false,
37167     
37168     render : function(){
37169         // add the header.....
37170        
37171         Roo.tree.ColumnTree.superclass.render.apply(this);
37172         
37173         this.el.addClass('x-column-tree');
37174         
37175         this.headers = this.el.createChild(
37176             {cls:'x-tree-headers'},this.innerCt.dom);
37177    
37178         var cols = this.columns, c;
37179         var totalWidth = 0;
37180         this.headEls = [];
37181         var  len = cols.length;
37182         for(var i = 0; i < len; i++){
37183              c = cols[i];
37184              totalWidth += c.width;
37185             this.headEls.push(this.headers.createChild({
37186                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37187                  cn: {
37188                      cls:'x-tree-hd-text',
37189                      html: c.header
37190                  },
37191                  style:'width:'+(c.width-this.borderWidth)+'px;'
37192              }));
37193         }
37194         this.headers.createChild({cls:'x-clear'});
37195         // prevent floats from wrapping when clipped
37196         this.headers.setWidth(totalWidth);
37197         //this.innerCt.setWidth(totalWidth);
37198         this.innerCt.setStyle({ overflow: 'auto' });
37199         this.onResize(this.width, this.height);
37200              
37201         
37202     },
37203     onResize : function(w,h)
37204     {
37205         this.height = h;
37206         this.width = w;
37207         // resize cols..
37208         this.innerCt.setWidth(this.width);
37209         this.innerCt.setHeight(this.height-20);
37210         
37211         // headers...
37212         var cols = this.columns, c;
37213         var totalWidth = 0;
37214         var expEl = false;
37215         var len = cols.length;
37216         for(var i = 0; i < len; i++){
37217             c = cols[i];
37218             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37219                 // it's the expander..
37220                 expEl  = this.headEls[i];
37221                 continue;
37222             }
37223             totalWidth += c.width;
37224             
37225         }
37226         if (expEl) {
37227             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37228         }
37229         this.headers.setWidth(w-20);
37230
37231         
37232         
37233         
37234     }
37235 });
37236 /*
37237  * Based on:
37238  * Ext JS Library 1.1.1
37239  * Copyright(c) 2006-2007, Ext JS, LLC.
37240  *
37241  * Originally Released Under LGPL - original licence link has changed is not relivant.
37242  *
37243  * Fork - LGPL
37244  * <script type="text/javascript">
37245  */
37246  
37247 /**
37248  * @class Roo.menu.Menu
37249  * @extends Roo.util.Observable
37250  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37251  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37252  * @constructor
37253  * Creates a new Menu
37254  * @param {Object} config Configuration options
37255  */
37256 Roo.menu.Menu = function(config){
37257     
37258     Roo.menu.Menu.superclass.constructor.call(this, config);
37259     
37260     this.id = this.id || Roo.id();
37261     this.addEvents({
37262         /**
37263          * @event beforeshow
37264          * Fires before this menu is displayed
37265          * @param {Roo.menu.Menu} this
37266          */
37267         beforeshow : true,
37268         /**
37269          * @event beforehide
37270          * Fires before this menu is hidden
37271          * @param {Roo.menu.Menu} this
37272          */
37273         beforehide : true,
37274         /**
37275          * @event show
37276          * Fires after this menu is displayed
37277          * @param {Roo.menu.Menu} this
37278          */
37279         show : true,
37280         /**
37281          * @event hide
37282          * Fires after this menu is hidden
37283          * @param {Roo.menu.Menu} this
37284          */
37285         hide : true,
37286         /**
37287          * @event click
37288          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37289          * @param {Roo.menu.Menu} this
37290          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37291          * @param {Roo.EventObject} e
37292          */
37293         click : true,
37294         /**
37295          * @event mouseover
37296          * Fires when the mouse is hovering over this menu
37297          * @param {Roo.menu.Menu} this
37298          * @param {Roo.EventObject} e
37299          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37300          */
37301         mouseover : true,
37302         /**
37303          * @event mouseout
37304          * Fires when the mouse exits this menu
37305          * @param {Roo.menu.Menu} this
37306          * @param {Roo.EventObject} e
37307          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37308          */
37309         mouseout : true,
37310         /**
37311          * @event itemclick
37312          * Fires when a menu item contained in this menu is clicked
37313          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37314          * @param {Roo.EventObject} e
37315          */
37316         itemclick: true
37317     });
37318     if (this.registerMenu) {
37319         Roo.menu.MenuMgr.register(this);
37320     }
37321     
37322     var mis = this.items;
37323     this.items = new Roo.util.MixedCollection();
37324     if(mis){
37325         this.add.apply(this, mis);
37326     }
37327 };
37328
37329 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37330     /**
37331      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37332      */
37333     minWidth : 120,
37334     /**
37335      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37336      * for bottom-right shadow (defaults to "sides")
37337      */
37338     shadow : "sides",
37339     /**
37340      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37341      * this menu (defaults to "tl-tr?")
37342      */
37343     subMenuAlign : "tl-tr?",
37344     /**
37345      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37346      * relative to its element of origin (defaults to "tl-bl?")
37347      */
37348     defaultAlign : "tl-bl?",
37349     /**
37350      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37351      */
37352     allowOtherMenus : false,
37353     /**
37354      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37355      */
37356     registerMenu : true,
37357
37358     hidden:true,
37359
37360     // private
37361     render : function(){
37362         if(this.el){
37363             return;
37364         }
37365         var el = this.el = new Roo.Layer({
37366             cls: "x-menu",
37367             shadow:this.shadow,
37368             constrain: false,
37369             parentEl: this.parentEl || document.body,
37370             zindex:15000
37371         });
37372
37373         this.keyNav = new Roo.menu.MenuNav(this);
37374
37375         if(this.plain){
37376             el.addClass("x-menu-plain");
37377         }
37378         if(this.cls){
37379             el.addClass(this.cls);
37380         }
37381         // generic focus element
37382         this.focusEl = el.createChild({
37383             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37384         });
37385         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37386         //disabling touch- as it's causing issues ..
37387         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37388         ul.on('click'   , this.onClick, this);
37389         
37390         
37391         ul.on("mouseover", this.onMouseOver, this);
37392         ul.on("mouseout", this.onMouseOut, this);
37393         this.items.each(function(item){
37394             if (item.hidden) {
37395                 return;
37396             }
37397             
37398             var li = document.createElement("li");
37399             li.className = "x-menu-list-item";
37400             ul.dom.appendChild(li);
37401             item.render(li, this);
37402         }, this);
37403         this.ul = ul;
37404         this.autoWidth();
37405     },
37406
37407     // private
37408     autoWidth : function(){
37409         var el = this.el, ul = this.ul;
37410         if(!el){
37411             return;
37412         }
37413         var w = this.width;
37414         if(w){
37415             el.setWidth(w);
37416         }else if(Roo.isIE){
37417             el.setWidth(this.minWidth);
37418             var t = el.dom.offsetWidth; // force recalc
37419             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37420         }
37421     },
37422
37423     // private
37424     delayAutoWidth : function(){
37425         if(this.rendered){
37426             if(!this.awTask){
37427                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37428             }
37429             this.awTask.delay(20);
37430         }
37431     },
37432
37433     // private
37434     findTargetItem : function(e){
37435         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37436         if(t && t.menuItemId){
37437             return this.items.get(t.menuItemId);
37438         }
37439     },
37440
37441     // private
37442     onClick : function(e){
37443         Roo.log("menu.onClick");
37444         var t = this.findTargetItem(e);
37445         if(!t){
37446             return;
37447         }
37448         Roo.log(e);
37449         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37450             if(t == this.activeItem && t.shouldDeactivate(e)){
37451                 this.activeItem.deactivate();
37452                 delete this.activeItem;
37453                 return;
37454             }
37455             if(t.canActivate){
37456                 this.setActiveItem(t, true);
37457             }
37458             return;
37459             
37460             
37461         }
37462         
37463         t.onClick(e);
37464         this.fireEvent("click", this, t, e);
37465     },
37466
37467     // private
37468     setActiveItem : function(item, autoExpand){
37469         if(item != this.activeItem){
37470             if(this.activeItem){
37471                 this.activeItem.deactivate();
37472             }
37473             this.activeItem = item;
37474             item.activate(autoExpand);
37475         }else if(autoExpand){
37476             item.expandMenu();
37477         }
37478     },
37479
37480     // private
37481     tryActivate : function(start, step){
37482         var items = this.items;
37483         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37484             var item = items.get(i);
37485             if(!item.disabled && item.canActivate){
37486                 this.setActiveItem(item, false);
37487                 return item;
37488             }
37489         }
37490         return false;
37491     },
37492
37493     // private
37494     onMouseOver : function(e){
37495         var t;
37496         if(t = this.findTargetItem(e)){
37497             if(t.canActivate && !t.disabled){
37498                 this.setActiveItem(t, true);
37499             }
37500         }
37501         this.fireEvent("mouseover", this, e, t);
37502     },
37503
37504     // private
37505     onMouseOut : function(e){
37506         var t;
37507         if(t = this.findTargetItem(e)){
37508             if(t == this.activeItem && t.shouldDeactivate(e)){
37509                 this.activeItem.deactivate();
37510                 delete this.activeItem;
37511             }
37512         }
37513         this.fireEvent("mouseout", this, e, t);
37514     },
37515
37516     /**
37517      * Read-only.  Returns true if the menu is currently displayed, else false.
37518      * @type Boolean
37519      */
37520     isVisible : function(){
37521         return this.el && !this.hidden;
37522     },
37523
37524     /**
37525      * Displays this menu relative to another element
37526      * @param {String/HTMLElement/Roo.Element} element The element to align to
37527      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37528      * the element (defaults to this.defaultAlign)
37529      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37530      */
37531     show : function(el, pos, parentMenu){
37532         this.parentMenu = parentMenu;
37533         if(!this.el){
37534             this.render();
37535         }
37536         this.fireEvent("beforeshow", this);
37537         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37538     },
37539
37540     /**
37541      * Displays this menu at a specific xy position
37542      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37543      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37544      */
37545     showAt : function(xy, parentMenu, /* private: */_e){
37546         this.parentMenu = parentMenu;
37547         if(!this.el){
37548             this.render();
37549         }
37550         if(_e !== false){
37551             this.fireEvent("beforeshow", this);
37552             xy = this.el.adjustForConstraints(xy);
37553         }
37554         this.el.setXY(xy);
37555         this.el.show();
37556         this.hidden = false;
37557         this.focus();
37558         this.fireEvent("show", this);
37559     },
37560
37561     focus : function(){
37562         if(!this.hidden){
37563             this.doFocus.defer(50, this);
37564         }
37565     },
37566
37567     doFocus : function(){
37568         if(!this.hidden){
37569             this.focusEl.focus();
37570         }
37571     },
37572
37573     /**
37574      * Hides this menu and optionally all parent menus
37575      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37576      */
37577     hide : function(deep){
37578         if(this.el && this.isVisible()){
37579             this.fireEvent("beforehide", this);
37580             if(this.activeItem){
37581                 this.activeItem.deactivate();
37582                 this.activeItem = null;
37583             }
37584             this.el.hide();
37585             this.hidden = true;
37586             this.fireEvent("hide", this);
37587         }
37588         if(deep === true && this.parentMenu){
37589             this.parentMenu.hide(true);
37590         }
37591     },
37592
37593     /**
37594      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37595      * Any of the following are valid:
37596      * <ul>
37597      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37598      * <li>An HTMLElement object which will be converted to a menu item</li>
37599      * <li>A menu item config object that will be created as a new menu item</li>
37600      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37601      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37602      * </ul>
37603      * Usage:
37604      * <pre><code>
37605 // Create the menu
37606 var menu = new Roo.menu.Menu();
37607
37608 // Create a menu item to add by reference
37609 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37610
37611 // Add a bunch of items at once using different methods.
37612 // Only the last item added will be returned.
37613 var item = menu.add(
37614     menuItem,                // add existing item by ref
37615     'Dynamic Item',          // new TextItem
37616     '-',                     // new separator
37617     { text: 'Config Item' }  // new item by config
37618 );
37619 </code></pre>
37620      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37621      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37622      */
37623     add : function(){
37624         var a = arguments, l = a.length, item;
37625         for(var i = 0; i < l; i++){
37626             var el = a[i];
37627             if ((typeof(el) == "object") && el.xtype && el.xns) {
37628                 el = Roo.factory(el, Roo.menu);
37629             }
37630             
37631             if(el.render){ // some kind of Item
37632                 item = this.addItem(el);
37633             }else if(typeof el == "string"){ // string
37634                 if(el == "separator" || el == "-"){
37635                     item = this.addSeparator();
37636                 }else{
37637                     item = this.addText(el);
37638                 }
37639             }else if(el.tagName || el.el){ // element
37640                 item = this.addElement(el);
37641             }else if(typeof el == "object"){ // must be menu item config?
37642                 item = this.addMenuItem(el);
37643             }
37644         }
37645         return item;
37646     },
37647
37648     /**
37649      * Returns this menu's underlying {@link Roo.Element} object
37650      * @return {Roo.Element} The element
37651      */
37652     getEl : function(){
37653         if(!this.el){
37654             this.render();
37655         }
37656         return this.el;
37657     },
37658
37659     /**
37660      * Adds a separator bar to the menu
37661      * @return {Roo.menu.Item} The menu item that was added
37662      */
37663     addSeparator : function(){
37664         return this.addItem(new Roo.menu.Separator());
37665     },
37666
37667     /**
37668      * Adds an {@link Roo.Element} object to the menu
37669      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37670      * @return {Roo.menu.Item} The menu item that was added
37671      */
37672     addElement : function(el){
37673         return this.addItem(new Roo.menu.BaseItem(el));
37674     },
37675
37676     /**
37677      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37678      * @param {Roo.menu.Item} item The menu item to add
37679      * @return {Roo.menu.Item} The menu item that was added
37680      */
37681     addItem : function(item){
37682         this.items.add(item);
37683         if(this.ul){
37684             var li = document.createElement("li");
37685             li.className = "x-menu-list-item";
37686             this.ul.dom.appendChild(li);
37687             item.render(li, this);
37688             this.delayAutoWidth();
37689         }
37690         return item;
37691     },
37692
37693     /**
37694      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37695      * @param {Object} config A MenuItem config object
37696      * @return {Roo.menu.Item} The menu item that was added
37697      */
37698     addMenuItem : function(config){
37699         if(!(config instanceof Roo.menu.Item)){
37700             if(typeof config.checked == "boolean"){ // must be check menu item config?
37701                 config = new Roo.menu.CheckItem(config);
37702             }else{
37703                 config = new Roo.menu.Item(config);
37704             }
37705         }
37706         return this.addItem(config);
37707     },
37708
37709     /**
37710      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37711      * @param {String} text The text to display in the menu item
37712      * @return {Roo.menu.Item} The menu item that was added
37713      */
37714     addText : function(text){
37715         return this.addItem(new Roo.menu.TextItem({ text : text }));
37716     },
37717
37718     /**
37719      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37720      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37721      * @param {Roo.menu.Item} item The menu item to add
37722      * @return {Roo.menu.Item} The menu item that was added
37723      */
37724     insert : function(index, item){
37725         this.items.insert(index, item);
37726         if(this.ul){
37727             var li = document.createElement("li");
37728             li.className = "x-menu-list-item";
37729             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37730             item.render(li, this);
37731             this.delayAutoWidth();
37732         }
37733         return item;
37734     },
37735
37736     /**
37737      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37738      * @param {Roo.menu.Item} item The menu item to remove
37739      */
37740     remove : function(item){
37741         this.items.removeKey(item.id);
37742         item.destroy();
37743     },
37744
37745     /**
37746      * Removes and destroys all items in the menu
37747      */
37748     removeAll : function(){
37749         var f;
37750         while(f = this.items.first()){
37751             this.remove(f);
37752         }
37753     }
37754 });
37755
37756 // MenuNav is a private utility class used internally by the Menu
37757 Roo.menu.MenuNav = function(menu){
37758     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37759     this.scope = this.menu = menu;
37760 };
37761
37762 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37763     doRelay : function(e, h){
37764         var k = e.getKey();
37765         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37766             this.menu.tryActivate(0, 1);
37767             return false;
37768         }
37769         return h.call(this.scope || this, e, this.menu);
37770     },
37771
37772     up : function(e, m){
37773         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37774             m.tryActivate(m.items.length-1, -1);
37775         }
37776     },
37777
37778     down : function(e, m){
37779         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37780             m.tryActivate(0, 1);
37781         }
37782     },
37783
37784     right : function(e, m){
37785         if(m.activeItem){
37786             m.activeItem.expandMenu(true);
37787         }
37788     },
37789
37790     left : function(e, m){
37791         m.hide();
37792         if(m.parentMenu && m.parentMenu.activeItem){
37793             m.parentMenu.activeItem.activate();
37794         }
37795     },
37796
37797     enter : function(e, m){
37798         if(m.activeItem){
37799             e.stopPropagation();
37800             m.activeItem.onClick(e);
37801             m.fireEvent("click", this, m.activeItem);
37802             return true;
37803         }
37804     }
37805 });/*
37806  * Based on:
37807  * Ext JS Library 1.1.1
37808  * Copyright(c) 2006-2007, Ext JS, LLC.
37809  *
37810  * Originally Released Under LGPL - original licence link has changed is not relivant.
37811  *
37812  * Fork - LGPL
37813  * <script type="text/javascript">
37814  */
37815  
37816 /**
37817  * @class Roo.menu.MenuMgr
37818  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
37819  * @singleton
37820  */
37821 Roo.menu.MenuMgr = function(){
37822    var menus, active, groups = {}, attached = false, lastShow = new Date();
37823
37824    // private - called when first menu is created
37825    function init(){
37826        menus = {};
37827        active = new Roo.util.MixedCollection();
37828        Roo.get(document).addKeyListener(27, function(){
37829            if(active.length > 0){
37830                hideAll();
37831            }
37832        });
37833    }
37834
37835    // private
37836    function hideAll(){
37837        if(active && active.length > 0){
37838            var c = active.clone();
37839            c.each(function(m){
37840                m.hide();
37841            });
37842        }
37843    }
37844
37845    // private
37846    function onHide(m){
37847        active.remove(m);
37848        if(active.length < 1){
37849            Roo.get(document).un("mousedown", onMouseDown);
37850            attached = false;
37851        }
37852    }
37853
37854    // private
37855    function onShow(m){
37856        var last = active.last();
37857        lastShow = new Date();
37858        active.add(m);
37859        if(!attached){
37860            Roo.get(document).on("mousedown", onMouseDown);
37861            attached = true;
37862        }
37863        if(m.parentMenu){
37864           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
37865           m.parentMenu.activeChild = m;
37866        }else if(last && last.isVisible()){
37867           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
37868        }
37869    }
37870
37871    // private
37872    function onBeforeHide(m){
37873        if(m.activeChild){
37874            m.activeChild.hide();
37875        }
37876        if(m.autoHideTimer){
37877            clearTimeout(m.autoHideTimer);
37878            delete m.autoHideTimer;
37879        }
37880    }
37881
37882    // private
37883    function onBeforeShow(m){
37884        var pm = m.parentMenu;
37885        if(!pm && !m.allowOtherMenus){
37886            hideAll();
37887        }else if(pm && pm.activeChild && active != m){
37888            pm.activeChild.hide();
37889        }
37890    }
37891
37892    // private
37893    function onMouseDown(e){
37894        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
37895            hideAll();
37896        }
37897    }
37898
37899    // private
37900    function onBeforeCheck(mi, state){
37901        if(state){
37902            var g = groups[mi.group];
37903            for(var i = 0, l = g.length; i < l; i++){
37904                if(g[i] != mi){
37905                    g[i].setChecked(false);
37906                }
37907            }
37908        }
37909    }
37910
37911    return {
37912
37913        /**
37914         * Hides all menus that are currently visible
37915         */
37916        hideAll : function(){
37917             hideAll();  
37918        },
37919
37920        // private
37921        register : function(menu){
37922            if(!menus){
37923                init();
37924            }
37925            menus[menu.id] = menu;
37926            menu.on("beforehide", onBeforeHide);
37927            menu.on("hide", onHide);
37928            menu.on("beforeshow", onBeforeShow);
37929            menu.on("show", onShow);
37930            var g = menu.group;
37931            if(g && menu.events["checkchange"]){
37932                if(!groups[g]){
37933                    groups[g] = [];
37934                }
37935                groups[g].push(menu);
37936                menu.on("checkchange", onCheck);
37937            }
37938        },
37939
37940         /**
37941          * Returns a {@link Roo.menu.Menu} object
37942          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
37943          * be used to generate and return a new Menu instance.
37944          */
37945        get : function(menu){
37946            if(typeof menu == "string"){ // menu id
37947                return menus[menu];
37948            }else if(menu.events){  // menu instance
37949                return menu;
37950            }else if(typeof menu.length == 'number'){ // array of menu items?
37951                return new Roo.menu.Menu({items:menu});
37952            }else{ // otherwise, must be a config
37953                return new Roo.menu.Menu(menu);
37954            }
37955        },
37956
37957        // private
37958        unregister : function(menu){
37959            delete menus[menu.id];
37960            menu.un("beforehide", onBeforeHide);
37961            menu.un("hide", onHide);
37962            menu.un("beforeshow", onBeforeShow);
37963            menu.un("show", onShow);
37964            var g = menu.group;
37965            if(g && menu.events["checkchange"]){
37966                groups[g].remove(menu);
37967                menu.un("checkchange", onCheck);
37968            }
37969        },
37970
37971        // private
37972        registerCheckable : function(menuItem){
37973            var g = menuItem.group;
37974            if(g){
37975                if(!groups[g]){
37976                    groups[g] = [];
37977                }
37978                groups[g].push(menuItem);
37979                menuItem.on("beforecheckchange", onBeforeCheck);
37980            }
37981        },
37982
37983        // private
37984        unregisterCheckable : function(menuItem){
37985            var g = menuItem.group;
37986            if(g){
37987                groups[g].remove(menuItem);
37988                menuItem.un("beforecheckchange", onBeforeCheck);
37989            }
37990        }
37991    };
37992 }();/*
37993  * Based on:
37994  * Ext JS Library 1.1.1
37995  * Copyright(c) 2006-2007, Ext JS, LLC.
37996  *
37997  * Originally Released Under LGPL - original licence link has changed is not relivant.
37998  *
37999  * Fork - LGPL
38000  * <script type="text/javascript">
38001  */
38002  
38003
38004 /**
38005  * @class Roo.menu.BaseItem
38006  * @extends Roo.Component
38007  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38008  * management and base configuration options shared by all menu components.
38009  * @constructor
38010  * Creates a new BaseItem
38011  * @param {Object} config Configuration options
38012  */
38013 Roo.menu.BaseItem = function(config){
38014     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38015
38016     this.addEvents({
38017         /**
38018          * @event click
38019          * Fires when this item is clicked
38020          * @param {Roo.menu.BaseItem} this
38021          * @param {Roo.EventObject} e
38022          */
38023         click: true,
38024         /**
38025          * @event activate
38026          * Fires when this item is activated
38027          * @param {Roo.menu.BaseItem} this
38028          */
38029         activate : true,
38030         /**
38031          * @event deactivate
38032          * Fires when this item is deactivated
38033          * @param {Roo.menu.BaseItem} this
38034          */
38035         deactivate : true
38036     });
38037
38038     if(this.handler){
38039         this.on("click", this.handler, this.scope, true);
38040     }
38041 };
38042
38043 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38044     /**
38045      * @cfg {Function} handler
38046      * A function that will handle the click event of this menu item (defaults to undefined)
38047      */
38048     /**
38049      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38050      */
38051     canActivate : false,
38052     
38053      /**
38054      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38055      */
38056     hidden: false,
38057     
38058     /**
38059      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38060      */
38061     activeClass : "x-menu-item-active",
38062     /**
38063      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38064      */
38065     hideOnClick : true,
38066     /**
38067      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38068      */
38069     hideDelay : 100,
38070
38071     // private
38072     ctype: "Roo.menu.BaseItem",
38073
38074     // private
38075     actionMode : "container",
38076
38077     // private
38078     render : function(container, parentMenu){
38079         this.parentMenu = parentMenu;
38080         Roo.menu.BaseItem.superclass.render.call(this, container);
38081         this.container.menuItemId = this.id;
38082     },
38083
38084     // private
38085     onRender : function(container, position){
38086         this.el = Roo.get(this.el);
38087         container.dom.appendChild(this.el.dom);
38088     },
38089
38090     // private
38091     onClick : function(e){
38092         if(!this.disabled && this.fireEvent("click", this, e) !== false
38093                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38094             this.handleClick(e);
38095         }else{
38096             e.stopEvent();
38097         }
38098     },
38099
38100     // private
38101     activate : function(){
38102         if(this.disabled){
38103             return false;
38104         }
38105         var li = this.container;
38106         li.addClass(this.activeClass);
38107         this.region = li.getRegion().adjust(2, 2, -2, -2);
38108         this.fireEvent("activate", this);
38109         return true;
38110     },
38111
38112     // private
38113     deactivate : function(){
38114         this.container.removeClass(this.activeClass);
38115         this.fireEvent("deactivate", this);
38116     },
38117
38118     // private
38119     shouldDeactivate : function(e){
38120         return !this.region || !this.region.contains(e.getPoint());
38121     },
38122
38123     // private
38124     handleClick : function(e){
38125         if(this.hideOnClick){
38126             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38127         }
38128     },
38129
38130     // private
38131     expandMenu : function(autoActivate){
38132         // do nothing
38133     },
38134
38135     // private
38136     hideMenu : function(){
38137         // do nothing
38138     }
38139 });/*
38140  * Based on:
38141  * Ext JS Library 1.1.1
38142  * Copyright(c) 2006-2007, Ext JS, LLC.
38143  *
38144  * Originally Released Under LGPL - original licence link has changed is not relivant.
38145  *
38146  * Fork - LGPL
38147  * <script type="text/javascript">
38148  */
38149  
38150 /**
38151  * @class Roo.menu.Adapter
38152  * @extends Roo.menu.BaseItem
38153  * 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.
38154  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38155  * @constructor
38156  * Creates a new Adapter
38157  * @param {Object} config Configuration options
38158  */
38159 Roo.menu.Adapter = function(component, config){
38160     Roo.menu.Adapter.superclass.constructor.call(this, config);
38161     this.component = component;
38162 };
38163 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38164     // private
38165     canActivate : true,
38166
38167     // private
38168     onRender : function(container, position){
38169         this.component.render(container);
38170         this.el = this.component.getEl();
38171     },
38172
38173     // private
38174     activate : function(){
38175         if(this.disabled){
38176             return false;
38177         }
38178         this.component.focus();
38179         this.fireEvent("activate", this);
38180         return true;
38181     },
38182
38183     // private
38184     deactivate : function(){
38185         this.fireEvent("deactivate", this);
38186     },
38187
38188     // private
38189     disable : function(){
38190         this.component.disable();
38191         Roo.menu.Adapter.superclass.disable.call(this);
38192     },
38193
38194     // private
38195     enable : function(){
38196         this.component.enable();
38197         Roo.menu.Adapter.superclass.enable.call(this);
38198     }
38199 });/*
38200  * Based on:
38201  * Ext JS Library 1.1.1
38202  * Copyright(c) 2006-2007, Ext JS, LLC.
38203  *
38204  * Originally Released Under LGPL - original licence link has changed is not relivant.
38205  *
38206  * Fork - LGPL
38207  * <script type="text/javascript">
38208  */
38209
38210 /**
38211  * @class Roo.menu.TextItem
38212  * @extends Roo.menu.BaseItem
38213  * Adds a static text string to a menu, usually used as either a heading or group separator.
38214  * Note: old style constructor with text is still supported.
38215  * 
38216  * @constructor
38217  * Creates a new TextItem
38218  * @param {Object} cfg Configuration
38219  */
38220 Roo.menu.TextItem = function(cfg){
38221     if (typeof(cfg) == 'string') {
38222         this.text = cfg;
38223     } else {
38224         Roo.apply(this,cfg);
38225     }
38226     
38227     Roo.menu.TextItem.superclass.constructor.call(this);
38228 };
38229
38230 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38231     /**
38232      * @cfg {Boolean} text Text to show on item.
38233      */
38234     text : '',
38235     
38236     /**
38237      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38238      */
38239     hideOnClick : false,
38240     /**
38241      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38242      */
38243     itemCls : "x-menu-text",
38244
38245     // private
38246     onRender : function(){
38247         var s = document.createElement("span");
38248         s.className = this.itemCls;
38249         s.innerHTML = this.text;
38250         this.el = s;
38251         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38252     }
38253 });/*
38254  * Based on:
38255  * Ext JS Library 1.1.1
38256  * Copyright(c) 2006-2007, Ext JS, LLC.
38257  *
38258  * Originally Released Under LGPL - original licence link has changed is not relivant.
38259  *
38260  * Fork - LGPL
38261  * <script type="text/javascript">
38262  */
38263
38264 /**
38265  * @class Roo.menu.Separator
38266  * @extends Roo.menu.BaseItem
38267  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38268  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38269  * @constructor
38270  * @param {Object} config Configuration options
38271  */
38272 Roo.menu.Separator = function(config){
38273     Roo.menu.Separator.superclass.constructor.call(this, config);
38274 };
38275
38276 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38277     /**
38278      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38279      */
38280     itemCls : "x-menu-sep",
38281     /**
38282      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38283      */
38284     hideOnClick : false,
38285
38286     // private
38287     onRender : function(li){
38288         var s = document.createElement("span");
38289         s.className = this.itemCls;
38290         s.innerHTML = "&#160;";
38291         this.el = s;
38292         li.addClass("x-menu-sep-li");
38293         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38294     }
38295 });/*
38296  * Based on:
38297  * Ext JS Library 1.1.1
38298  * Copyright(c) 2006-2007, Ext JS, LLC.
38299  *
38300  * Originally Released Under LGPL - original licence link has changed is not relivant.
38301  *
38302  * Fork - LGPL
38303  * <script type="text/javascript">
38304  */
38305 /**
38306  * @class Roo.menu.Item
38307  * @extends Roo.menu.BaseItem
38308  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38309  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38310  * activation and click handling.
38311  * @constructor
38312  * Creates a new Item
38313  * @param {Object} config Configuration options
38314  */
38315 Roo.menu.Item = function(config){
38316     Roo.menu.Item.superclass.constructor.call(this, config);
38317     if(this.menu){
38318         this.menu = Roo.menu.MenuMgr.get(this.menu);
38319     }
38320 };
38321 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38322     
38323     /**
38324      * @cfg {String} text
38325      * The text to show on the menu item.
38326      */
38327     text: '',
38328      /**
38329      * @cfg {String} HTML to render in menu
38330      * The text to show on the menu item (HTML version).
38331      */
38332     html: '',
38333     /**
38334      * @cfg {String} icon
38335      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38336      */
38337     icon: undefined,
38338     /**
38339      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38340      */
38341     itemCls : "x-menu-item",
38342     /**
38343      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38344      */
38345     canActivate : true,
38346     /**
38347      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38348      */
38349     showDelay: 200,
38350     // doc'd in BaseItem
38351     hideDelay: 200,
38352
38353     // private
38354     ctype: "Roo.menu.Item",
38355     
38356     // private
38357     onRender : function(container, position){
38358         var el = document.createElement("a");
38359         el.hideFocus = true;
38360         el.unselectable = "on";
38361         el.href = this.href || "#";
38362         if(this.hrefTarget){
38363             el.target = this.hrefTarget;
38364         }
38365         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38366         
38367         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38368         
38369         el.innerHTML = String.format(
38370                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38371                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38372         this.el = el;
38373         Roo.menu.Item.superclass.onRender.call(this, container, position);
38374     },
38375
38376     /**
38377      * Sets the text to display in this menu item
38378      * @param {String} text The text to display
38379      * @param {Boolean} isHTML true to indicate text is pure html.
38380      */
38381     setText : function(text, isHTML){
38382         if (isHTML) {
38383             this.html = text;
38384         } else {
38385             this.text = text;
38386             this.html = '';
38387         }
38388         if(this.rendered){
38389             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38390      
38391             this.el.update(String.format(
38392                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38393                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38394             this.parentMenu.autoWidth();
38395         }
38396     },
38397
38398     // private
38399     handleClick : function(e){
38400         if(!this.href){ // if no link defined, stop the event automatically
38401             e.stopEvent();
38402         }
38403         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38404     },
38405
38406     // private
38407     activate : function(autoExpand){
38408         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38409             this.focus();
38410             if(autoExpand){
38411                 this.expandMenu();
38412             }
38413         }
38414         return true;
38415     },
38416
38417     // private
38418     shouldDeactivate : function(e){
38419         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38420             if(this.menu && this.menu.isVisible()){
38421                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38422             }
38423             return true;
38424         }
38425         return false;
38426     },
38427
38428     // private
38429     deactivate : function(){
38430         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38431         this.hideMenu();
38432     },
38433
38434     // private
38435     expandMenu : function(autoActivate){
38436         if(!this.disabled && this.menu){
38437             clearTimeout(this.hideTimer);
38438             delete this.hideTimer;
38439             if(!this.menu.isVisible() && !this.showTimer){
38440                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38441             }else if (this.menu.isVisible() && autoActivate){
38442                 this.menu.tryActivate(0, 1);
38443             }
38444         }
38445     },
38446
38447     // private
38448     deferExpand : function(autoActivate){
38449         delete this.showTimer;
38450         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38451         if(autoActivate){
38452             this.menu.tryActivate(0, 1);
38453         }
38454     },
38455
38456     // private
38457     hideMenu : function(){
38458         clearTimeout(this.showTimer);
38459         delete this.showTimer;
38460         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38461             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38462         }
38463     },
38464
38465     // private
38466     deferHide : function(){
38467         delete this.hideTimer;
38468         this.menu.hide();
38469     }
38470 });/*
38471  * Based on:
38472  * Ext JS Library 1.1.1
38473  * Copyright(c) 2006-2007, Ext JS, LLC.
38474  *
38475  * Originally Released Under LGPL - original licence link has changed is not relivant.
38476  *
38477  * Fork - LGPL
38478  * <script type="text/javascript">
38479  */
38480  
38481 /**
38482  * @class Roo.menu.CheckItem
38483  * @extends Roo.menu.Item
38484  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38485  * @constructor
38486  * Creates a new CheckItem
38487  * @param {Object} config Configuration options
38488  */
38489 Roo.menu.CheckItem = function(config){
38490     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38491     this.addEvents({
38492         /**
38493          * @event beforecheckchange
38494          * Fires before the checked value is set, providing an opportunity to cancel if needed
38495          * @param {Roo.menu.CheckItem} this
38496          * @param {Boolean} checked The new checked value that will be set
38497          */
38498         "beforecheckchange" : true,
38499         /**
38500          * @event checkchange
38501          * Fires after the checked value has been set
38502          * @param {Roo.menu.CheckItem} this
38503          * @param {Boolean} checked The checked value that was set
38504          */
38505         "checkchange" : true
38506     });
38507     if(this.checkHandler){
38508         this.on('checkchange', this.checkHandler, this.scope);
38509     }
38510 };
38511 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38512     /**
38513      * @cfg {String} group
38514      * All check items with the same group name will automatically be grouped into a single-select
38515      * radio button group (defaults to '')
38516      */
38517     /**
38518      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38519      */
38520     itemCls : "x-menu-item x-menu-check-item",
38521     /**
38522      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38523      */
38524     groupClass : "x-menu-group-item",
38525
38526     /**
38527      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38528      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38529      * initialized with checked = true will be rendered as checked.
38530      */
38531     checked: false,
38532
38533     // private
38534     ctype: "Roo.menu.CheckItem",
38535
38536     // private
38537     onRender : function(c){
38538         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38539         if(this.group){
38540             this.el.addClass(this.groupClass);
38541         }
38542         Roo.menu.MenuMgr.registerCheckable(this);
38543         if(this.checked){
38544             this.checked = false;
38545             this.setChecked(true, true);
38546         }
38547     },
38548
38549     // private
38550     destroy : function(){
38551         if(this.rendered){
38552             Roo.menu.MenuMgr.unregisterCheckable(this);
38553         }
38554         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38555     },
38556
38557     /**
38558      * Set the checked state of this item
38559      * @param {Boolean} checked The new checked value
38560      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38561      */
38562     setChecked : function(state, suppressEvent){
38563         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38564             if(this.container){
38565                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38566             }
38567             this.checked = state;
38568             if(suppressEvent !== true){
38569                 this.fireEvent("checkchange", this, state);
38570             }
38571         }
38572     },
38573
38574     // private
38575     handleClick : function(e){
38576        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38577            this.setChecked(!this.checked);
38578        }
38579        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38580     }
38581 });/*
38582  * Based on:
38583  * Ext JS Library 1.1.1
38584  * Copyright(c) 2006-2007, Ext JS, LLC.
38585  *
38586  * Originally Released Under LGPL - original licence link has changed is not relivant.
38587  *
38588  * Fork - LGPL
38589  * <script type="text/javascript">
38590  */
38591  
38592 /**
38593  * @class Roo.menu.DateItem
38594  * @extends Roo.menu.Adapter
38595  * A menu item that wraps the {@link Roo.DatPicker} component.
38596  * @constructor
38597  * Creates a new DateItem
38598  * @param {Object} config Configuration options
38599  */
38600 Roo.menu.DateItem = function(config){
38601     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38602     /** The Roo.DatePicker object @type Roo.DatePicker */
38603     this.picker = this.component;
38604     this.addEvents({select: true});
38605     
38606     this.picker.on("render", function(picker){
38607         picker.getEl().swallowEvent("click");
38608         picker.container.addClass("x-menu-date-item");
38609     });
38610
38611     this.picker.on("select", this.onSelect, this);
38612 };
38613
38614 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38615     // private
38616     onSelect : function(picker, date){
38617         this.fireEvent("select", this, date, picker);
38618         Roo.menu.DateItem.superclass.handleClick.call(this);
38619     }
38620 });/*
38621  * Based on:
38622  * Ext JS Library 1.1.1
38623  * Copyright(c) 2006-2007, Ext JS, LLC.
38624  *
38625  * Originally Released Under LGPL - original licence link has changed is not relivant.
38626  *
38627  * Fork - LGPL
38628  * <script type="text/javascript">
38629  */
38630  
38631 /**
38632  * @class Roo.menu.ColorItem
38633  * @extends Roo.menu.Adapter
38634  * A menu item that wraps the {@link Roo.ColorPalette} component.
38635  * @constructor
38636  * Creates a new ColorItem
38637  * @param {Object} config Configuration options
38638  */
38639 Roo.menu.ColorItem = function(config){
38640     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38641     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38642     this.palette = this.component;
38643     this.relayEvents(this.palette, ["select"]);
38644     if(this.selectHandler){
38645         this.on('select', this.selectHandler, this.scope);
38646     }
38647 };
38648 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38649  * Based on:
38650  * Ext JS Library 1.1.1
38651  * Copyright(c) 2006-2007, Ext JS, LLC.
38652  *
38653  * Originally Released Under LGPL - original licence link has changed is not relivant.
38654  *
38655  * Fork - LGPL
38656  * <script type="text/javascript">
38657  */
38658  
38659
38660 /**
38661  * @class Roo.menu.DateMenu
38662  * @extends Roo.menu.Menu
38663  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38664  * @constructor
38665  * Creates a new DateMenu
38666  * @param {Object} config Configuration options
38667  */
38668 Roo.menu.DateMenu = function(config){
38669     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38670     this.plain = true;
38671     var di = new Roo.menu.DateItem(config);
38672     this.add(di);
38673     /**
38674      * The {@link Roo.DatePicker} instance for this DateMenu
38675      * @type DatePicker
38676      */
38677     this.picker = di.picker;
38678     /**
38679      * @event select
38680      * @param {DatePicker} picker
38681      * @param {Date} date
38682      */
38683     this.relayEvents(di, ["select"]);
38684     this.on('beforeshow', function(){
38685         if(this.picker){
38686             this.picker.hideMonthPicker(false);
38687         }
38688     }, this);
38689 };
38690 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38691     cls:'x-date-menu'
38692 });/*
38693  * Based on:
38694  * Ext JS Library 1.1.1
38695  * Copyright(c) 2006-2007, Ext JS, LLC.
38696  *
38697  * Originally Released Under LGPL - original licence link has changed is not relivant.
38698  *
38699  * Fork - LGPL
38700  * <script type="text/javascript">
38701  */
38702  
38703
38704 /**
38705  * @class Roo.menu.ColorMenu
38706  * @extends Roo.menu.Menu
38707  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38708  * @constructor
38709  * Creates a new ColorMenu
38710  * @param {Object} config Configuration options
38711  */
38712 Roo.menu.ColorMenu = function(config){
38713     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38714     this.plain = true;
38715     var ci = new Roo.menu.ColorItem(config);
38716     this.add(ci);
38717     /**
38718      * The {@link Roo.ColorPalette} instance for this ColorMenu
38719      * @type ColorPalette
38720      */
38721     this.palette = ci.palette;
38722     /**
38723      * @event select
38724      * @param {ColorPalette} palette
38725      * @param {String} color
38726      */
38727     this.relayEvents(ci, ["select"]);
38728 };
38729 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38730  * Based on:
38731  * Ext JS Library 1.1.1
38732  * Copyright(c) 2006-2007, Ext JS, LLC.
38733  *
38734  * Originally Released Under LGPL - original licence link has changed is not relivant.
38735  *
38736  * Fork - LGPL
38737  * <script type="text/javascript">
38738  */
38739  
38740 /**
38741  * @class Roo.form.TextItem
38742  * @extends Roo.BoxComponent
38743  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38744  * @constructor
38745  * Creates a new TextItem
38746  * @param {Object} config Configuration options
38747  */
38748 Roo.form.TextItem = function(config){
38749     Roo.form.TextItem.superclass.constructor.call(this, config);
38750 };
38751
38752 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38753     
38754     /**
38755      * @cfg {String} tag the tag for this item (default div)
38756      */
38757     tag : 'div',
38758     /**
38759      * @cfg {String} html the content for this item
38760      */
38761     html : '',
38762     
38763     getAutoCreate : function()
38764     {
38765         var cfg = {
38766             id: this.id,
38767             tag: this.tag,
38768             html: this.html,
38769             cls: 'x-form-item'
38770         };
38771         
38772         return cfg;
38773         
38774     },
38775     
38776     onRender : function(ct, position)
38777     {
38778         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38779         
38780         if(!this.el){
38781             var cfg = this.getAutoCreate();
38782             if(!cfg.name){
38783                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38784             }
38785             if (!cfg.name.length) {
38786                 delete cfg.name;
38787             }
38788             this.el = ct.createChild(cfg, position);
38789         }
38790     }
38791     
38792 });/*
38793  * Based on:
38794  * Ext JS Library 1.1.1
38795  * Copyright(c) 2006-2007, Ext JS, LLC.
38796  *
38797  * Originally Released Under LGPL - original licence link has changed is not relivant.
38798  *
38799  * Fork - LGPL
38800  * <script type="text/javascript">
38801  */
38802  
38803 /**
38804  * @class Roo.form.Field
38805  * @extends Roo.BoxComponent
38806  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38807  * @constructor
38808  * Creates a new Field
38809  * @param {Object} config Configuration options
38810  */
38811 Roo.form.Field = function(config){
38812     Roo.form.Field.superclass.constructor.call(this, config);
38813 };
38814
38815 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
38816     /**
38817      * @cfg {String} fieldLabel Label to use when rendering a form.
38818      */
38819        /**
38820      * @cfg {String} qtip Mouse over tip
38821      */
38822      
38823     /**
38824      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
38825      */
38826     invalidClass : "x-form-invalid",
38827     /**
38828      * @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")
38829      */
38830     invalidText : "The value in this field is invalid",
38831     /**
38832      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
38833      */
38834     focusClass : "x-form-focus",
38835     /**
38836      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
38837       automatic validation (defaults to "keyup").
38838      */
38839     validationEvent : "keyup",
38840     /**
38841      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
38842      */
38843     validateOnBlur : true,
38844     /**
38845      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
38846      */
38847     validationDelay : 250,
38848     /**
38849      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38850      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
38851      */
38852     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
38853     /**
38854      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
38855      */
38856     fieldClass : "x-form-field",
38857     /**
38858      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
38859      *<pre>
38860 Value         Description
38861 -----------   ----------------------------------------------------------------------
38862 qtip          Display a quick tip when the user hovers over the field
38863 title         Display a default browser title attribute popup
38864 under         Add a block div beneath the field containing the error text
38865 side          Add an error icon to the right of the field with a popup on hover
38866 [element id]  Add the error text directly to the innerHTML of the specified element
38867 </pre>
38868      */
38869     msgTarget : 'qtip',
38870     /**
38871      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
38872      */
38873     msgFx : 'normal',
38874
38875     /**
38876      * @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.
38877      */
38878     readOnly : false,
38879
38880     /**
38881      * @cfg {Boolean} disabled True to disable the field (defaults to false).
38882      */
38883     disabled : false,
38884
38885     /**
38886      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
38887      */
38888     inputType : undefined,
38889     
38890     /**
38891      * @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).
38892          */
38893         tabIndex : undefined,
38894         
38895     // private
38896     isFormField : true,
38897
38898     // private
38899     hasFocus : false,
38900     /**
38901      * @property {Roo.Element} fieldEl
38902      * Element Containing the rendered Field (with label etc.)
38903      */
38904     /**
38905      * @cfg {Mixed} value A value to initialize this field with.
38906      */
38907     value : undefined,
38908
38909     /**
38910      * @cfg {String} name The field's HTML name attribute.
38911      */
38912     /**
38913      * @cfg {String} cls A CSS class to apply to the field's underlying element.
38914      */
38915     // private
38916     loadedValue : false,
38917      
38918      
38919         // private ??
38920         initComponent : function(){
38921         Roo.form.Field.superclass.initComponent.call(this);
38922         this.addEvents({
38923             /**
38924              * @event focus
38925              * Fires when this field receives input focus.
38926              * @param {Roo.form.Field} this
38927              */
38928             focus : true,
38929             /**
38930              * @event blur
38931              * Fires when this field loses input focus.
38932              * @param {Roo.form.Field} this
38933              */
38934             blur : true,
38935             /**
38936              * @event specialkey
38937              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
38938              * {@link Roo.EventObject#getKey} to determine which key was pressed.
38939              * @param {Roo.form.Field} this
38940              * @param {Roo.EventObject} e The event object
38941              */
38942             specialkey : true,
38943             /**
38944              * @event change
38945              * Fires just before the field blurs if the field value has changed.
38946              * @param {Roo.form.Field} this
38947              * @param {Mixed} newValue The new value
38948              * @param {Mixed} oldValue The original value
38949              */
38950             change : true,
38951             /**
38952              * @event invalid
38953              * Fires after the field has been marked as invalid.
38954              * @param {Roo.form.Field} this
38955              * @param {String} msg The validation message
38956              */
38957             invalid : true,
38958             /**
38959              * @event valid
38960              * Fires after the field has been validated with no errors.
38961              * @param {Roo.form.Field} this
38962              */
38963             valid : true,
38964              /**
38965              * @event keyup
38966              * Fires after the key up
38967              * @param {Roo.form.Field} this
38968              * @param {Roo.EventObject}  e The event Object
38969              */
38970             keyup : true
38971         });
38972     },
38973
38974     /**
38975      * Returns the name attribute of the field if available
38976      * @return {String} name The field name
38977      */
38978     getName: function(){
38979          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
38980     },
38981
38982     // private
38983     onRender : function(ct, position){
38984         Roo.form.Field.superclass.onRender.call(this, ct, position);
38985         if(!this.el){
38986             var cfg = this.getAutoCreate();
38987             if(!cfg.name){
38988                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
38989             }
38990             if (!cfg.name.length) {
38991                 delete cfg.name;
38992             }
38993             if(this.inputType){
38994                 cfg.type = this.inputType;
38995             }
38996             this.el = ct.createChild(cfg, position);
38997         }
38998         var type = this.el.dom.type;
38999         if(type){
39000             if(type == 'password'){
39001                 type = 'text';
39002             }
39003             this.el.addClass('x-form-'+type);
39004         }
39005         if(this.readOnly){
39006             this.el.dom.readOnly = true;
39007         }
39008         if(this.tabIndex !== undefined){
39009             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39010         }
39011
39012         this.el.addClass([this.fieldClass, this.cls]);
39013         this.initValue();
39014     },
39015
39016     /**
39017      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39018      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39019      * @return {Roo.form.Field} this
39020      */
39021     applyTo : function(target){
39022         this.allowDomMove = false;
39023         this.el = Roo.get(target);
39024         this.render(this.el.dom.parentNode);
39025         return this;
39026     },
39027
39028     // private
39029     initValue : function(){
39030         if(this.value !== undefined){
39031             this.setValue(this.value);
39032         }else if(this.el.dom.value.length > 0){
39033             this.setValue(this.el.dom.value);
39034         }
39035     },
39036
39037     /**
39038      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39039      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39040      */
39041     isDirty : function() {
39042         if(this.disabled) {
39043             return false;
39044         }
39045         return String(this.getValue()) !== String(this.originalValue);
39046     },
39047
39048     /**
39049      * stores the current value in loadedValue
39050      */
39051     resetHasChanged : function()
39052     {
39053         this.loadedValue = String(this.getValue());
39054     },
39055     /**
39056      * checks the current value against the 'loaded' value.
39057      * Note - will return false if 'resetHasChanged' has not been called first.
39058      */
39059     hasChanged : function()
39060     {
39061         if(this.disabled || this.readOnly) {
39062             return false;
39063         }
39064         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39065     },
39066     
39067     
39068     
39069     // private
39070     afterRender : function(){
39071         Roo.form.Field.superclass.afterRender.call(this);
39072         this.initEvents();
39073     },
39074
39075     // private
39076     fireKey : function(e){
39077         //Roo.log('field ' + e.getKey());
39078         if(e.isNavKeyPress()){
39079             this.fireEvent("specialkey", this, e);
39080         }
39081     },
39082
39083     /**
39084      * Resets the current field value to the originally loaded value and clears any validation messages
39085      */
39086     reset : function(){
39087         this.setValue(this.resetValue);
39088         this.originalValue = this.getValue();
39089         this.clearInvalid();
39090     },
39091
39092     // private
39093     initEvents : function(){
39094         // safari killled keypress - so keydown is now used..
39095         this.el.on("keydown" , this.fireKey,  this);
39096         this.el.on("focus", this.onFocus,  this);
39097         this.el.on("blur", this.onBlur,  this);
39098         this.el.relayEvent('keyup', this);
39099
39100         // reference to original value for reset
39101         this.originalValue = this.getValue();
39102         this.resetValue =  this.getValue();
39103     },
39104
39105     // private
39106     onFocus : function(){
39107         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39108             this.el.addClass(this.focusClass);
39109         }
39110         if(!this.hasFocus){
39111             this.hasFocus = true;
39112             this.startValue = this.getValue();
39113             this.fireEvent("focus", this);
39114         }
39115     },
39116
39117     beforeBlur : Roo.emptyFn,
39118
39119     // private
39120     onBlur : function(){
39121         this.beforeBlur();
39122         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39123             this.el.removeClass(this.focusClass);
39124         }
39125         this.hasFocus = false;
39126         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39127             this.validate();
39128         }
39129         var v = this.getValue();
39130         if(String(v) !== String(this.startValue)){
39131             this.fireEvent('change', this, v, this.startValue);
39132         }
39133         this.fireEvent("blur", this);
39134     },
39135
39136     /**
39137      * Returns whether or not the field value is currently valid
39138      * @param {Boolean} preventMark True to disable marking the field invalid
39139      * @return {Boolean} True if the value is valid, else false
39140      */
39141     isValid : function(preventMark){
39142         if(this.disabled){
39143             return true;
39144         }
39145         var restore = this.preventMark;
39146         this.preventMark = preventMark === true;
39147         var v = this.validateValue(this.processValue(this.getRawValue()));
39148         this.preventMark = restore;
39149         return v;
39150     },
39151
39152     /**
39153      * Validates the field value
39154      * @return {Boolean} True if the value is valid, else false
39155      */
39156     validate : function(){
39157         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39158             this.clearInvalid();
39159             return true;
39160         }
39161         return false;
39162     },
39163
39164     processValue : function(value){
39165         return value;
39166     },
39167
39168     // private
39169     // Subclasses should provide the validation implementation by overriding this
39170     validateValue : function(value){
39171         return true;
39172     },
39173
39174     /**
39175      * Mark this field as invalid
39176      * @param {String} msg The validation message
39177      */
39178     markInvalid : function(msg){
39179         if(!this.rendered || this.preventMark){ // not rendered
39180             return;
39181         }
39182         
39183         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39184         
39185         obj.el.addClass(this.invalidClass);
39186         msg = msg || this.invalidText;
39187         switch(this.msgTarget){
39188             case 'qtip':
39189                 obj.el.dom.qtip = msg;
39190                 obj.el.dom.qclass = 'x-form-invalid-tip';
39191                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39192                     Roo.QuickTips.enable();
39193                 }
39194                 break;
39195             case 'title':
39196                 this.el.dom.title = msg;
39197                 break;
39198             case 'under':
39199                 if(!this.errorEl){
39200                     var elp = this.el.findParent('.x-form-element', 5, true);
39201                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39202                     this.errorEl.setWidth(elp.getWidth(true)-20);
39203                 }
39204                 this.errorEl.update(msg);
39205                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39206                 break;
39207             case 'side':
39208                 if(!this.errorIcon){
39209                     var elp = this.el.findParent('.x-form-element', 5, true);
39210                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39211                 }
39212                 this.alignErrorIcon();
39213                 this.errorIcon.dom.qtip = msg;
39214                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39215                 this.errorIcon.show();
39216                 this.on('resize', this.alignErrorIcon, this);
39217                 break;
39218             default:
39219                 var t = Roo.getDom(this.msgTarget);
39220                 t.innerHTML = msg;
39221                 t.style.display = this.msgDisplay;
39222                 break;
39223         }
39224         this.fireEvent('invalid', this, msg);
39225     },
39226
39227     // private
39228     alignErrorIcon : function(){
39229         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39230     },
39231
39232     /**
39233      * Clear any invalid styles/messages for this field
39234      */
39235     clearInvalid : function(){
39236         if(!this.rendered || this.preventMark){ // not rendered
39237             return;
39238         }
39239         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39240         
39241         obj.el.removeClass(this.invalidClass);
39242         switch(this.msgTarget){
39243             case 'qtip':
39244                 obj.el.dom.qtip = '';
39245                 break;
39246             case 'title':
39247                 this.el.dom.title = '';
39248                 break;
39249             case 'under':
39250                 if(this.errorEl){
39251                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39252                 }
39253                 break;
39254             case 'side':
39255                 if(this.errorIcon){
39256                     this.errorIcon.dom.qtip = '';
39257                     this.errorIcon.hide();
39258                     this.un('resize', this.alignErrorIcon, this);
39259                 }
39260                 break;
39261             default:
39262                 var t = Roo.getDom(this.msgTarget);
39263                 t.innerHTML = '';
39264                 t.style.display = 'none';
39265                 break;
39266         }
39267         this.fireEvent('valid', this);
39268     },
39269
39270     /**
39271      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39272      * @return {Mixed} value The field value
39273      */
39274     getRawValue : function(){
39275         var v = this.el.getValue();
39276         
39277         return v;
39278     },
39279
39280     /**
39281      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39282      * @return {Mixed} value The field value
39283      */
39284     getValue : function(){
39285         var v = this.el.getValue();
39286          
39287         return v;
39288     },
39289
39290     /**
39291      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39292      * @param {Mixed} value The value to set
39293      */
39294     setRawValue : function(v){
39295         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39296     },
39297
39298     /**
39299      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39300      * @param {Mixed} value The value to set
39301      */
39302     setValue : function(v){
39303         this.value = v;
39304         if(this.rendered){
39305             this.el.dom.value = (v === null || v === undefined ? '' : v);
39306              this.validate();
39307         }
39308     },
39309
39310     adjustSize : function(w, h){
39311         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39312         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39313         return s;
39314     },
39315
39316     adjustWidth : function(tag, w){
39317         tag = tag.toLowerCase();
39318         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39319             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39320                 if(tag == 'input'){
39321                     return w + 2;
39322                 }
39323                 if(tag == 'textarea'){
39324                     return w-2;
39325                 }
39326             }else if(Roo.isOpera){
39327                 if(tag == 'input'){
39328                     return w + 2;
39329                 }
39330                 if(tag == 'textarea'){
39331                     return w-2;
39332                 }
39333             }
39334         }
39335         return w;
39336     }
39337 });
39338
39339
39340 // anything other than normal should be considered experimental
39341 Roo.form.Field.msgFx = {
39342     normal : {
39343         show: function(msgEl, f){
39344             msgEl.setDisplayed('block');
39345         },
39346
39347         hide : function(msgEl, f){
39348             msgEl.setDisplayed(false).update('');
39349         }
39350     },
39351
39352     slide : {
39353         show: function(msgEl, f){
39354             msgEl.slideIn('t', {stopFx:true});
39355         },
39356
39357         hide : function(msgEl, f){
39358             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39359         }
39360     },
39361
39362     slideRight : {
39363         show: function(msgEl, f){
39364             msgEl.fixDisplay();
39365             msgEl.alignTo(f.el, 'tl-tr');
39366             msgEl.slideIn('l', {stopFx:true});
39367         },
39368
39369         hide : function(msgEl, f){
39370             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39371         }
39372     }
39373 };/*
39374  * Based on:
39375  * Ext JS Library 1.1.1
39376  * Copyright(c) 2006-2007, Ext JS, LLC.
39377  *
39378  * Originally Released Under LGPL - original licence link has changed is not relivant.
39379  *
39380  * Fork - LGPL
39381  * <script type="text/javascript">
39382  */
39383  
39384
39385 /**
39386  * @class Roo.form.TextField
39387  * @extends Roo.form.Field
39388  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39389  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39390  * @constructor
39391  * Creates a new TextField
39392  * @param {Object} config Configuration options
39393  */
39394 Roo.form.TextField = function(config){
39395     Roo.form.TextField.superclass.constructor.call(this, config);
39396     this.addEvents({
39397         /**
39398          * @event autosize
39399          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39400          * according to the default logic, but this event provides a hook for the developer to apply additional
39401          * logic at runtime to resize the field if needed.
39402              * @param {Roo.form.Field} this This text field
39403              * @param {Number} width The new field width
39404              */
39405         autosize : true
39406     });
39407 };
39408
39409 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39410     /**
39411      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39412      */
39413     grow : false,
39414     /**
39415      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39416      */
39417     growMin : 30,
39418     /**
39419      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39420      */
39421     growMax : 800,
39422     /**
39423      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39424      */
39425     vtype : null,
39426     /**
39427      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39428      */
39429     maskRe : null,
39430     /**
39431      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39432      */
39433     disableKeyFilter : false,
39434     /**
39435      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39436      */
39437     allowBlank : true,
39438     /**
39439      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39440      */
39441     minLength : 0,
39442     /**
39443      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39444      */
39445     maxLength : Number.MAX_VALUE,
39446     /**
39447      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39448      */
39449     minLengthText : "The minimum length for this field is {0}",
39450     /**
39451      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39452      */
39453     maxLengthText : "The maximum length for this field is {0}",
39454     /**
39455      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39456      */
39457     selectOnFocus : false,
39458     /**
39459      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39460      */    
39461     allowLeadingSpace : false,
39462     /**
39463      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39464      */
39465     blankText : "This field is required",
39466     /**
39467      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39468      * If available, this function will be called only after the basic validators all return true, and will be passed the
39469      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39470      */
39471     validator : null,
39472     /**
39473      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39474      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39475      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39476      */
39477     regex : null,
39478     /**
39479      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39480      */
39481     regexText : "",
39482     /**
39483      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39484      */
39485     emptyText : null,
39486    
39487
39488     // private
39489     initEvents : function()
39490     {
39491         if (this.emptyText) {
39492             this.el.attr('placeholder', this.emptyText);
39493         }
39494         
39495         Roo.form.TextField.superclass.initEvents.call(this);
39496         if(this.validationEvent == 'keyup'){
39497             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39498             this.el.on('keyup', this.filterValidation, this);
39499         }
39500         else if(this.validationEvent !== false){
39501             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39502         }
39503         
39504         if(this.selectOnFocus){
39505             this.on("focus", this.preFocus, this);
39506         }
39507         if (!this.allowLeadingSpace) {
39508             this.on('blur', this.cleanLeadingSpace, this);
39509         }
39510         
39511         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39512             this.el.on("keypress", this.filterKeys, this);
39513         }
39514         if(this.grow){
39515             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39516             this.el.on("click", this.autoSize,  this);
39517         }
39518         if(this.el.is('input[type=password]') && Roo.isSafari){
39519             this.el.on('keydown', this.SafariOnKeyDown, this);
39520         }
39521     },
39522
39523     processValue : function(value){
39524         if(this.stripCharsRe){
39525             var newValue = value.replace(this.stripCharsRe, '');
39526             if(newValue !== value){
39527                 this.setRawValue(newValue);
39528                 return newValue;
39529             }
39530         }
39531         return value;
39532     },
39533
39534     filterValidation : function(e){
39535         if(!e.isNavKeyPress()){
39536             this.validationTask.delay(this.validationDelay);
39537         }
39538     },
39539
39540     // private
39541     onKeyUp : function(e){
39542         if(!e.isNavKeyPress()){
39543             this.autoSize();
39544         }
39545     },
39546     // private - clean the leading white space
39547     cleanLeadingSpace : function(e)
39548     {
39549         if ( this.inputType == 'file') {
39550             return;
39551         }
39552         
39553         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39554     },
39555     /**
39556      * Resets the current field value to the originally-loaded value and clears any validation messages.
39557      *  
39558      */
39559     reset : function(){
39560         Roo.form.TextField.superclass.reset.call(this);
39561        
39562     }, 
39563     // private
39564     preFocus : function(){
39565         
39566         if(this.selectOnFocus){
39567             this.el.dom.select();
39568         }
39569     },
39570
39571     
39572     // private
39573     filterKeys : function(e){
39574         var k = e.getKey();
39575         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39576             return;
39577         }
39578         var c = e.getCharCode(), cc = String.fromCharCode(c);
39579         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39580             return;
39581         }
39582         if(!this.maskRe.test(cc)){
39583             e.stopEvent();
39584         }
39585     },
39586
39587     setValue : function(v){
39588         
39589         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39590         
39591         this.autoSize();
39592     },
39593
39594     /**
39595      * Validates a value according to the field's validation rules and marks the field as invalid
39596      * if the validation fails
39597      * @param {Mixed} value The value to validate
39598      * @return {Boolean} True if the value is valid, else false
39599      */
39600     validateValue : function(value){
39601         if(value.length < 1)  { // if it's blank
39602              if(this.allowBlank){
39603                 this.clearInvalid();
39604                 return true;
39605              }else{
39606                 this.markInvalid(this.blankText);
39607                 return false;
39608              }
39609         }
39610         if(value.length < this.minLength){
39611             this.markInvalid(String.format(this.minLengthText, this.minLength));
39612             return false;
39613         }
39614         if(value.length > this.maxLength){
39615             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39616             return false;
39617         }
39618         if(this.vtype){
39619             var vt = Roo.form.VTypes;
39620             if(!vt[this.vtype](value, this)){
39621                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39622                 return false;
39623             }
39624         }
39625         if(typeof this.validator == "function"){
39626             var msg = this.validator(value);
39627             if(msg !== true){
39628                 this.markInvalid(msg);
39629                 return false;
39630             }
39631         }
39632         if(this.regex && !this.regex.test(value)){
39633             this.markInvalid(this.regexText);
39634             return false;
39635         }
39636         return true;
39637     },
39638
39639     /**
39640      * Selects text in this field
39641      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39642      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39643      */
39644     selectText : function(start, end){
39645         var v = this.getRawValue();
39646         if(v.length > 0){
39647             start = start === undefined ? 0 : start;
39648             end = end === undefined ? v.length : end;
39649             var d = this.el.dom;
39650             if(d.setSelectionRange){
39651                 d.setSelectionRange(start, end);
39652             }else if(d.createTextRange){
39653                 var range = d.createTextRange();
39654                 range.moveStart("character", start);
39655                 range.moveEnd("character", v.length-end);
39656                 range.select();
39657             }
39658         }
39659     },
39660
39661     /**
39662      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39663      * This only takes effect if grow = true, and fires the autosize event.
39664      */
39665     autoSize : function(){
39666         if(!this.grow || !this.rendered){
39667             return;
39668         }
39669         if(!this.metrics){
39670             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39671         }
39672         var el = this.el;
39673         var v = el.dom.value;
39674         var d = document.createElement('div');
39675         d.appendChild(document.createTextNode(v));
39676         v = d.innerHTML;
39677         d = null;
39678         v += "&#160;";
39679         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39680         this.el.setWidth(w);
39681         this.fireEvent("autosize", this, w);
39682     },
39683     
39684     // private
39685     SafariOnKeyDown : function(event)
39686     {
39687         // this is a workaround for a password hang bug on chrome/ webkit.
39688         
39689         var isSelectAll = false;
39690         
39691         if(this.el.dom.selectionEnd > 0){
39692             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39693         }
39694         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39695             event.preventDefault();
39696             this.setValue('');
39697             return;
39698         }
39699         
39700         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39701             
39702             event.preventDefault();
39703             // this is very hacky as keydown always get's upper case.
39704             
39705             var cc = String.fromCharCode(event.getCharCode());
39706             
39707             
39708             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39709             
39710         }
39711         
39712         
39713     }
39714 });/*
39715  * Based on:
39716  * Ext JS Library 1.1.1
39717  * Copyright(c) 2006-2007, Ext JS, LLC.
39718  *
39719  * Originally Released Under LGPL - original licence link has changed is not relivant.
39720  *
39721  * Fork - LGPL
39722  * <script type="text/javascript">
39723  */
39724  
39725 /**
39726  * @class Roo.form.Hidden
39727  * @extends Roo.form.TextField
39728  * Simple Hidden element used on forms 
39729  * 
39730  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39731  * 
39732  * @constructor
39733  * Creates a new Hidden form element.
39734  * @param {Object} config Configuration options
39735  */
39736
39737
39738
39739 // easy hidden field...
39740 Roo.form.Hidden = function(config){
39741     Roo.form.Hidden.superclass.constructor.call(this, config);
39742 };
39743   
39744 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39745     fieldLabel:      '',
39746     inputType:      'hidden',
39747     width:          50,
39748     allowBlank:     true,
39749     labelSeparator: '',
39750     hidden:         true,
39751     itemCls :       'x-form-item-display-none'
39752
39753
39754 });
39755
39756
39757 /*
39758  * Based on:
39759  * Ext JS Library 1.1.1
39760  * Copyright(c) 2006-2007, Ext JS, LLC.
39761  *
39762  * Originally Released Under LGPL - original licence link has changed is not relivant.
39763  *
39764  * Fork - LGPL
39765  * <script type="text/javascript">
39766  */
39767  
39768 /**
39769  * @class Roo.form.TriggerField
39770  * @extends Roo.form.TextField
39771  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39772  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
39773  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
39774  * for which you can provide a custom implementation.  For example:
39775  * <pre><code>
39776 var trigger = new Roo.form.TriggerField();
39777 trigger.onTriggerClick = myTriggerFn;
39778 trigger.applyTo('my-field');
39779 </code></pre>
39780  *
39781  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
39782  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
39783  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39784  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
39785  * @constructor
39786  * Create a new TriggerField.
39787  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
39788  * to the base TextField)
39789  */
39790 Roo.form.TriggerField = function(config){
39791     this.mimicing = false;
39792     Roo.form.TriggerField.superclass.constructor.call(this, config);
39793 };
39794
39795 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
39796     /**
39797      * @cfg {String} triggerClass A CSS class to apply to the trigger
39798      */
39799     /**
39800      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39801      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
39802      */
39803     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
39804     /**
39805      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
39806      */
39807     hideTrigger:false,
39808
39809     /** @cfg {Boolean} grow @hide */
39810     /** @cfg {Number} growMin @hide */
39811     /** @cfg {Number} growMax @hide */
39812
39813     /**
39814      * @hide 
39815      * @method
39816      */
39817     autoSize: Roo.emptyFn,
39818     // private
39819     monitorTab : true,
39820     // private
39821     deferHeight : true,
39822
39823     
39824     actionMode : 'wrap',
39825     // private
39826     onResize : function(w, h){
39827         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
39828         if(typeof w == 'number'){
39829             var x = w - this.trigger.getWidth();
39830             this.el.setWidth(this.adjustWidth('input', x));
39831             this.trigger.setStyle('left', x+'px');
39832         }
39833     },
39834
39835     // private
39836     adjustSize : Roo.BoxComponent.prototype.adjustSize,
39837
39838     // private
39839     getResizeEl : function(){
39840         return this.wrap;
39841     },
39842
39843     // private
39844     getPositionEl : function(){
39845         return this.wrap;
39846     },
39847
39848     // private
39849     alignErrorIcon : function(){
39850         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
39851     },
39852
39853     // private
39854     onRender : function(ct, position){
39855         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
39856         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
39857         this.trigger = this.wrap.createChild(this.triggerConfig ||
39858                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
39859         if(this.hideTrigger){
39860             this.trigger.setDisplayed(false);
39861         }
39862         this.initTrigger();
39863         if(!this.width){
39864             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
39865         }
39866     },
39867
39868     // private
39869     initTrigger : function(){
39870         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39871         this.trigger.addClassOnOver('x-form-trigger-over');
39872         this.trigger.addClassOnClick('x-form-trigger-click');
39873     },
39874
39875     // private
39876     onDestroy : function(){
39877         if(this.trigger){
39878             this.trigger.removeAllListeners();
39879             this.trigger.remove();
39880         }
39881         if(this.wrap){
39882             this.wrap.remove();
39883         }
39884         Roo.form.TriggerField.superclass.onDestroy.call(this);
39885     },
39886
39887     // private
39888     onFocus : function(){
39889         Roo.form.TriggerField.superclass.onFocus.call(this);
39890         if(!this.mimicing){
39891             this.wrap.addClass('x-trigger-wrap-focus');
39892             this.mimicing = true;
39893             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
39894             if(this.monitorTab){
39895                 this.el.on("keydown", this.checkTab, this);
39896             }
39897         }
39898     },
39899
39900     // private
39901     checkTab : function(e){
39902         if(e.getKey() == e.TAB){
39903             this.triggerBlur();
39904         }
39905     },
39906
39907     // private
39908     onBlur : function(){
39909         // do nothing
39910     },
39911
39912     // private
39913     mimicBlur : function(e, t){
39914         if(!this.wrap.contains(t) && this.validateBlur()){
39915             this.triggerBlur();
39916         }
39917     },
39918
39919     // private
39920     triggerBlur : function(){
39921         this.mimicing = false;
39922         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
39923         if(this.monitorTab){
39924             this.el.un("keydown", this.checkTab, this);
39925         }
39926         this.wrap.removeClass('x-trigger-wrap-focus');
39927         Roo.form.TriggerField.superclass.onBlur.call(this);
39928     },
39929
39930     // private
39931     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
39932     validateBlur : function(e, t){
39933         return true;
39934     },
39935
39936     // private
39937     onDisable : function(){
39938         Roo.form.TriggerField.superclass.onDisable.call(this);
39939         if(this.wrap){
39940             this.wrap.addClass('x-item-disabled');
39941         }
39942     },
39943
39944     // private
39945     onEnable : function(){
39946         Roo.form.TriggerField.superclass.onEnable.call(this);
39947         if(this.wrap){
39948             this.wrap.removeClass('x-item-disabled');
39949         }
39950     },
39951
39952     // private
39953     onShow : function(){
39954         var ae = this.getActionEl();
39955         
39956         if(ae){
39957             ae.dom.style.display = '';
39958             ae.dom.style.visibility = 'visible';
39959         }
39960     },
39961
39962     // private
39963     
39964     onHide : function(){
39965         var ae = this.getActionEl();
39966         ae.dom.style.display = 'none';
39967     },
39968
39969     /**
39970      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
39971      * by an implementing function.
39972      * @method
39973      * @param {EventObject} e
39974      */
39975     onTriggerClick : Roo.emptyFn
39976 });
39977
39978 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
39979 // to be extended by an implementing class.  For an example of implementing this class, see the custom
39980 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
39981 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
39982     initComponent : function(){
39983         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
39984
39985         this.triggerConfig = {
39986             tag:'span', cls:'x-form-twin-triggers', cn:[
39987             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
39988             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
39989         ]};
39990     },
39991
39992     getTrigger : function(index){
39993         return this.triggers[index];
39994     },
39995
39996     initTrigger : function(){
39997         var ts = this.trigger.select('.x-form-trigger', true);
39998         this.wrap.setStyle('overflow', 'hidden');
39999         var triggerField = this;
40000         ts.each(function(t, all, index){
40001             t.hide = function(){
40002                 var w = triggerField.wrap.getWidth();
40003                 this.dom.style.display = 'none';
40004                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40005             };
40006             t.show = function(){
40007                 var w = triggerField.wrap.getWidth();
40008                 this.dom.style.display = '';
40009                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40010             };
40011             var triggerIndex = 'Trigger'+(index+1);
40012
40013             if(this['hide'+triggerIndex]){
40014                 t.dom.style.display = 'none';
40015             }
40016             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40017             t.addClassOnOver('x-form-trigger-over');
40018             t.addClassOnClick('x-form-trigger-click');
40019         }, this);
40020         this.triggers = ts.elements;
40021     },
40022
40023     onTrigger1Click : Roo.emptyFn,
40024     onTrigger2Click : Roo.emptyFn
40025 });/*
40026  * Based on:
40027  * Ext JS Library 1.1.1
40028  * Copyright(c) 2006-2007, Ext JS, LLC.
40029  *
40030  * Originally Released Under LGPL - original licence link has changed is not relivant.
40031  *
40032  * Fork - LGPL
40033  * <script type="text/javascript">
40034  */
40035  
40036 /**
40037  * @class Roo.form.TextArea
40038  * @extends Roo.form.TextField
40039  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40040  * support for auto-sizing.
40041  * @constructor
40042  * Creates a new TextArea
40043  * @param {Object} config Configuration options
40044  */
40045 Roo.form.TextArea = function(config){
40046     Roo.form.TextArea.superclass.constructor.call(this, config);
40047     // these are provided exchanges for backwards compat
40048     // minHeight/maxHeight were replaced by growMin/growMax to be
40049     // compatible with TextField growing config values
40050     if(this.minHeight !== undefined){
40051         this.growMin = this.minHeight;
40052     }
40053     if(this.maxHeight !== undefined){
40054         this.growMax = this.maxHeight;
40055     }
40056 };
40057
40058 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40059     /**
40060      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40061      */
40062     growMin : 60,
40063     /**
40064      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40065      */
40066     growMax: 1000,
40067     /**
40068      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40069      * in the field (equivalent to setting overflow: hidden, defaults to false)
40070      */
40071     preventScrollbars: false,
40072     /**
40073      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40074      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40075      */
40076
40077     // private
40078     onRender : function(ct, position){
40079         if(!this.el){
40080             this.defaultAutoCreate = {
40081                 tag: "textarea",
40082                 style:"width:300px;height:60px;",
40083                 autocomplete: "new-password"
40084             };
40085         }
40086         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40087         if(this.grow){
40088             this.textSizeEl = Roo.DomHelper.append(document.body, {
40089                 tag: "pre", cls: "x-form-grow-sizer"
40090             });
40091             if(this.preventScrollbars){
40092                 this.el.setStyle("overflow", "hidden");
40093             }
40094             this.el.setHeight(this.growMin);
40095         }
40096     },
40097
40098     onDestroy : function(){
40099         if(this.textSizeEl){
40100             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40101         }
40102         Roo.form.TextArea.superclass.onDestroy.call(this);
40103     },
40104
40105     // private
40106     onKeyUp : function(e){
40107         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40108             this.autoSize();
40109         }
40110     },
40111
40112     /**
40113      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40114      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40115      */
40116     autoSize : function(){
40117         if(!this.grow || !this.textSizeEl){
40118             return;
40119         }
40120         var el = this.el;
40121         var v = el.dom.value;
40122         var ts = this.textSizeEl;
40123
40124         ts.innerHTML = '';
40125         ts.appendChild(document.createTextNode(v));
40126         v = ts.innerHTML;
40127
40128         Roo.fly(ts).setWidth(this.el.getWidth());
40129         if(v.length < 1){
40130             v = "&#160;&#160;";
40131         }else{
40132             if(Roo.isIE){
40133                 v = v.replace(/\n/g, '<p>&#160;</p>');
40134             }
40135             v += "&#160;\n&#160;";
40136         }
40137         ts.innerHTML = v;
40138         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40139         if(h != this.lastHeight){
40140             this.lastHeight = h;
40141             this.el.setHeight(h);
40142             this.fireEvent("autosize", this, h);
40143         }
40144     }
40145 });/*
40146  * Based on:
40147  * Ext JS Library 1.1.1
40148  * Copyright(c) 2006-2007, Ext JS, LLC.
40149  *
40150  * Originally Released Under LGPL - original licence link has changed is not relivant.
40151  *
40152  * Fork - LGPL
40153  * <script type="text/javascript">
40154  */
40155  
40156
40157 /**
40158  * @class Roo.form.NumberField
40159  * @extends Roo.form.TextField
40160  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40161  * @constructor
40162  * Creates a new NumberField
40163  * @param {Object} config Configuration options
40164  */
40165 Roo.form.NumberField = function(config){
40166     Roo.form.NumberField.superclass.constructor.call(this, config);
40167 };
40168
40169 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40170     /**
40171      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40172      */
40173     fieldClass: "x-form-field x-form-num-field",
40174     /**
40175      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40176      */
40177     allowDecimals : true,
40178     /**
40179      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40180      */
40181     decimalSeparator : ".",
40182     /**
40183      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40184      */
40185     decimalPrecision : 2,
40186     /**
40187      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40188      */
40189     allowNegative : true,
40190     /**
40191      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40192      */
40193     minValue : Number.NEGATIVE_INFINITY,
40194     /**
40195      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40196      */
40197     maxValue : Number.MAX_VALUE,
40198     /**
40199      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40200      */
40201     minText : "The minimum value for this field is {0}",
40202     /**
40203      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40204      */
40205     maxText : "The maximum value for this field is {0}",
40206     /**
40207      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40208      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40209      */
40210     nanText : "{0} is not a valid number",
40211
40212     // private
40213     initEvents : function(){
40214         Roo.form.NumberField.superclass.initEvents.call(this);
40215         var allowed = "0123456789";
40216         if(this.allowDecimals){
40217             allowed += this.decimalSeparator;
40218         }
40219         if(this.allowNegative){
40220             allowed += "-";
40221         }
40222         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40223         var keyPress = function(e){
40224             var k = e.getKey();
40225             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40226                 return;
40227             }
40228             var c = e.getCharCode();
40229             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40230                 e.stopEvent();
40231             }
40232         };
40233         this.el.on("keypress", keyPress, this);
40234     },
40235
40236     // private
40237     validateValue : function(value){
40238         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40239             return false;
40240         }
40241         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40242              return true;
40243         }
40244         var num = this.parseValue(value);
40245         if(isNaN(num)){
40246             this.markInvalid(String.format(this.nanText, value));
40247             return false;
40248         }
40249         if(num < this.minValue){
40250             this.markInvalid(String.format(this.minText, this.minValue));
40251             return false;
40252         }
40253         if(num > this.maxValue){
40254             this.markInvalid(String.format(this.maxText, this.maxValue));
40255             return false;
40256         }
40257         return true;
40258     },
40259
40260     getValue : function(){
40261         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40262     },
40263
40264     // private
40265     parseValue : function(value){
40266         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40267         return isNaN(value) ? '' : value;
40268     },
40269
40270     // private
40271     fixPrecision : function(value){
40272         var nan = isNaN(value);
40273         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40274             return nan ? '' : value;
40275         }
40276         return parseFloat(value).toFixed(this.decimalPrecision);
40277     },
40278
40279     setValue : function(v){
40280         v = this.fixPrecision(v);
40281         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40282     },
40283
40284     // private
40285     decimalPrecisionFcn : function(v){
40286         return Math.floor(v);
40287     },
40288
40289     beforeBlur : function(){
40290         var v = this.parseValue(this.getRawValue());
40291         if(v){
40292             this.setValue(v);
40293         }
40294     }
40295 });/*
40296  * Based on:
40297  * Ext JS Library 1.1.1
40298  * Copyright(c) 2006-2007, Ext JS, LLC.
40299  *
40300  * Originally Released Under LGPL - original licence link has changed is not relivant.
40301  *
40302  * Fork - LGPL
40303  * <script type="text/javascript">
40304  */
40305  
40306 /**
40307  * @class Roo.form.DateField
40308  * @extends Roo.form.TriggerField
40309  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40310 * @constructor
40311 * Create a new DateField
40312 * @param {Object} config
40313  */
40314 Roo.form.DateField = function(config)
40315 {
40316     Roo.form.DateField.superclass.constructor.call(this, config);
40317     
40318       this.addEvents({
40319          
40320         /**
40321          * @event select
40322          * Fires when a date is selected
40323              * @param {Roo.form.DateField} combo This combo box
40324              * @param {Date} date The date selected
40325              */
40326         'select' : true
40327          
40328     });
40329     
40330     
40331     if(typeof this.minValue == "string") {
40332         this.minValue = this.parseDate(this.minValue);
40333     }
40334     if(typeof this.maxValue == "string") {
40335         this.maxValue = this.parseDate(this.maxValue);
40336     }
40337     this.ddMatch = null;
40338     if(this.disabledDates){
40339         var dd = this.disabledDates;
40340         var re = "(?:";
40341         for(var i = 0; i < dd.length; i++){
40342             re += dd[i];
40343             if(i != dd.length-1) {
40344                 re += "|";
40345             }
40346         }
40347         this.ddMatch = new RegExp(re + ")");
40348     }
40349 };
40350
40351 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40352     /**
40353      * @cfg {String} format
40354      * The default date format string which can be overriden for localization support.  The format must be
40355      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40356      */
40357     format : "m/d/y",
40358     /**
40359      * @cfg {String} altFormats
40360      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40361      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40362      */
40363     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40364     /**
40365      * @cfg {Array} disabledDays
40366      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40367      */
40368     disabledDays : null,
40369     /**
40370      * @cfg {String} disabledDaysText
40371      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40372      */
40373     disabledDaysText : "Disabled",
40374     /**
40375      * @cfg {Array} disabledDates
40376      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40377      * expression so they are very powerful. Some examples:
40378      * <ul>
40379      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40380      * <li>["03/08", "09/16"] would disable those days for every year</li>
40381      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40382      * <li>["03/../2006"] would disable every day in March 2006</li>
40383      * <li>["^03"] would disable every day in every March</li>
40384      * </ul>
40385      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40386      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40387      */
40388     disabledDates : null,
40389     /**
40390      * @cfg {String} disabledDatesText
40391      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40392      */
40393     disabledDatesText : "Disabled",
40394     /**
40395      * @cfg {Date/String} minValue
40396      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40397      * valid format (defaults to null).
40398      */
40399     minValue : null,
40400     /**
40401      * @cfg {Date/String} maxValue
40402      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40403      * valid format (defaults to null).
40404      */
40405     maxValue : null,
40406     /**
40407      * @cfg {String} minText
40408      * The error text to display when the date in the cell is before minValue (defaults to
40409      * 'The date in this field must be after {minValue}').
40410      */
40411     minText : "The date in this field must be equal to or after {0}",
40412     /**
40413      * @cfg {String} maxText
40414      * The error text to display when the date in the cell is after maxValue (defaults to
40415      * 'The date in this field must be before {maxValue}').
40416      */
40417     maxText : "The date in this field must be equal to or before {0}",
40418     /**
40419      * @cfg {String} invalidText
40420      * The error text to display when the date in the field is invalid (defaults to
40421      * '{value} is not a valid date - it must be in the format {format}').
40422      */
40423     invalidText : "{0} is not a valid date - it must be in the format {1}",
40424     /**
40425      * @cfg {String} triggerClass
40426      * An additional CSS class used to style the trigger button.  The trigger will always get the
40427      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40428      * which displays a calendar icon).
40429      */
40430     triggerClass : 'x-form-date-trigger',
40431     
40432
40433     /**
40434      * @cfg {Boolean} useIso
40435      * if enabled, then the date field will use a hidden field to store the 
40436      * real value as iso formated date. default (false)
40437      */ 
40438     useIso : false,
40439     /**
40440      * @cfg {String/Object} autoCreate
40441      * A DomHelper element spec, or true for a default element spec (defaults to
40442      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40443      */ 
40444     // private
40445     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40446     
40447     // private
40448     hiddenField: false,
40449     
40450     onRender : function(ct, position)
40451     {
40452         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40453         if (this.useIso) {
40454             //this.el.dom.removeAttribute('name'); 
40455             Roo.log("Changing name?");
40456             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40457             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40458                     'before', true);
40459             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40460             // prevent input submission
40461             this.hiddenName = this.name;
40462         }
40463             
40464             
40465     },
40466     
40467     // private
40468     validateValue : function(value)
40469     {
40470         value = this.formatDate(value);
40471         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40472             Roo.log('super failed');
40473             return false;
40474         }
40475         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40476              return true;
40477         }
40478         var svalue = value;
40479         value = this.parseDate(value);
40480         if(!value){
40481             Roo.log('parse date failed' + svalue);
40482             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40483             return false;
40484         }
40485         var time = value.getTime();
40486         if(this.minValue && time < this.minValue.getTime()){
40487             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40488             return false;
40489         }
40490         if(this.maxValue && time > this.maxValue.getTime()){
40491             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40492             return false;
40493         }
40494         if(this.disabledDays){
40495             var day = value.getDay();
40496             for(var i = 0; i < this.disabledDays.length; i++) {
40497                 if(day === this.disabledDays[i]){
40498                     this.markInvalid(this.disabledDaysText);
40499                     return false;
40500                 }
40501             }
40502         }
40503         var fvalue = this.formatDate(value);
40504         if(this.ddMatch && this.ddMatch.test(fvalue)){
40505             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40506             return false;
40507         }
40508         return true;
40509     },
40510
40511     // private
40512     // Provides logic to override the default TriggerField.validateBlur which just returns true
40513     validateBlur : function(){
40514         return !this.menu || !this.menu.isVisible();
40515     },
40516     
40517     getName: function()
40518     {
40519         // returns hidden if it's set..
40520         if (!this.rendered) {return ''};
40521         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40522         
40523     },
40524
40525     /**
40526      * Returns the current date value of the date field.
40527      * @return {Date} The date value
40528      */
40529     getValue : function(){
40530         
40531         return  this.hiddenField ?
40532                 this.hiddenField.value :
40533                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40534     },
40535
40536     /**
40537      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40538      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40539      * (the default format used is "m/d/y").
40540      * <br />Usage:
40541      * <pre><code>
40542 //All of these calls set the same date value (May 4, 2006)
40543
40544 //Pass a date object:
40545 var dt = new Date('5/4/06');
40546 dateField.setValue(dt);
40547
40548 //Pass a date string (default format):
40549 dateField.setValue('5/4/06');
40550
40551 //Pass a date string (custom format):
40552 dateField.format = 'Y-m-d';
40553 dateField.setValue('2006-5-4');
40554 </code></pre>
40555      * @param {String/Date} date The date or valid date string
40556      */
40557     setValue : function(date){
40558         if (this.hiddenField) {
40559             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40560         }
40561         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40562         // make sure the value field is always stored as a date..
40563         this.value = this.parseDate(date);
40564         
40565         
40566     },
40567
40568     // private
40569     parseDate : function(value){
40570         if(!value || value instanceof Date){
40571             return value;
40572         }
40573         var v = Date.parseDate(value, this.format);
40574          if (!v && this.useIso) {
40575             v = Date.parseDate(value, 'Y-m-d');
40576         }
40577         if(!v && this.altFormats){
40578             if(!this.altFormatsArray){
40579                 this.altFormatsArray = this.altFormats.split("|");
40580             }
40581             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40582                 v = Date.parseDate(value, this.altFormatsArray[i]);
40583             }
40584         }
40585         return v;
40586     },
40587
40588     // private
40589     formatDate : function(date, fmt){
40590         return (!date || !(date instanceof Date)) ?
40591                date : date.dateFormat(fmt || this.format);
40592     },
40593
40594     // private
40595     menuListeners : {
40596         select: function(m, d){
40597             
40598             this.setValue(d);
40599             this.fireEvent('select', this, d);
40600         },
40601         show : function(){ // retain focus styling
40602             this.onFocus();
40603         },
40604         hide : function(){
40605             this.focus.defer(10, this);
40606             var ml = this.menuListeners;
40607             this.menu.un("select", ml.select,  this);
40608             this.menu.un("show", ml.show,  this);
40609             this.menu.un("hide", ml.hide,  this);
40610         }
40611     },
40612
40613     // private
40614     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40615     onTriggerClick : function(){
40616         if(this.disabled){
40617             return;
40618         }
40619         if(this.menu == null){
40620             this.menu = new Roo.menu.DateMenu();
40621         }
40622         Roo.apply(this.menu.picker,  {
40623             showClear: this.allowBlank,
40624             minDate : this.minValue,
40625             maxDate : this.maxValue,
40626             disabledDatesRE : this.ddMatch,
40627             disabledDatesText : this.disabledDatesText,
40628             disabledDays : this.disabledDays,
40629             disabledDaysText : this.disabledDaysText,
40630             format : this.useIso ? 'Y-m-d' : this.format,
40631             minText : String.format(this.minText, this.formatDate(this.minValue)),
40632             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40633         });
40634         this.menu.on(Roo.apply({}, this.menuListeners, {
40635             scope:this
40636         }));
40637         this.menu.picker.setValue(this.getValue() || new Date());
40638         this.menu.show(this.el, "tl-bl?");
40639     },
40640
40641     beforeBlur : function(){
40642         var v = this.parseDate(this.getRawValue());
40643         if(v){
40644             this.setValue(v);
40645         }
40646     },
40647
40648     /*@
40649      * overide
40650      * 
40651      */
40652     isDirty : function() {
40653         if(this.disabled) {
40654             return false;
40655         }
40656         
40657         if(typeof(this.startValue) === 'undefined'){
40658             return false;
40659         }
40660         
40661         return String(this.getValue()) !== String(this.startValue);
40662         
40663     },
40664     // @overide
40665     cleanLeadingSpace : function(e)
40666     {
40667        return;
40668     }
40669     
40670 });/*
40671  * Based on:
40672  * Ext JS Library 1.1.1
40673  * Copyright(c) 2006-2007, Ext JS, LLC.
40674  *
40675  * Originally Released Under LGPL - original licence link has changed is not relivant.
40676  *
40677  * Fork - LGPL
40678  * <script type="text/javascript">
40679  */
40680  
40681 /**
40682  * @class Roo.form.MonthField
40683  * @extends Roo.form.TriggerField
40684  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40685 * @constructor
40686 * Create a new MonthField
40687 * @param {Object} config
40688  */
40689 Roo.form.MonthField = function(config){
40690     
40691     Roo.form.MonthField.superclass.constructor.call(this, config);
40692     
40693       this.addEvents({
40694          
40695         /**
40696          * @event select
40697          * Fires when a date is selected
40698              * @param {Roo.form.MonthFieeld} combo This combo box
40699              * @param {Date} date The date selected
40700              */
40701         'select' : true
40702          
40703     });
40704     
40705     
40706     if(typeof this.minValue == "string") {
40707         this.minValue = this.parseDate(this.minValue);
40708     }
40709     if(typeof this.maxValue == "string") {
40710         this.maxValue = this.parseDate(this.maxValue);
40711     }
40712     this.ddMatch = null;
40713     if(this.disabledDates){
40714         var dd = this.disabledDates;
40715         var re = "(?:";
40716         for(var i = 0; i < dd.length; i++){
40717             re += dd[i];
40718             if(i != dd.length-1) {
40719                 re += "|";
40720             }
40721         }
40722         this.ddMatch = new RegExp(re + ")");
40723     }
40724 };
40725
40726 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40727     /**
40728      * @cfg {String} format
40729      * The default date format string which can be overriden for localization support.  The format must be
40730      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40731      */
40732     format : "M Y",
40733     /**
40734      * @cfg {String} altFormats
40735      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40736      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40737      */
40738     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40739     /**
40740      * @cfg {Array} disabledDays
40741      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40742      */
40743     disabledDays : [0,1,2,3,4,5,6],
40744     /**
40745      * @cfg {String} disabledDaysText
40746      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40747      */
40748     disabledDaysText : "Disabled",
40749     /**
40750      * @cfg {Array} disabledDates
40751      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40752      * expression so they are very powerful. Some examples:
40753      * <ul>
40754      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40755      * <li>["03/08", "09/16"] would disable those days for every year</li>
40756      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40757      * <li>["03/../2006"] would disable every day in March 2006</li>
40758      * <li>["^03"] would disable every day in every March</li>
40759      * </ul>
40760      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40761      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40762      */
40763     disabledDates : null,
40764     /**
40765      * @cfg {String} disabledDatesText
40766      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40767      */
40768     disabledDatesText : "Disabled",
40769     /**
40770      * @cfg {Date/String} minValue
40771      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40772      * valid format (defaults to null).
40773      */
40774     minValue : null,
40775     /**
40776      * @cfg {Date/String} maxValue
40777      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40778      * valid format (defaults to null).
40779      */
40780     maxValue : null,
40781     /**
40782      * @cfg {String} minText
40783      * The error text to display when the date in the cell is before minValue (defaults to
40784      * 'The date in this field must be after {minValue}').
40785      */
40786     minText : "The date in this field must be equal to or after {0}",
40787     /**
40788      * @cfg {String} maxTextf
40789      * The error text to display when the date in the cell is after maxValue (defaults to
40790      * 'The date in this field must be before {maxValue}').
40791      */
40792     maxText : "The date in this field must be equal to or before {0}",
40793     /**
40794      * @cfg {String} invalidText
40795      * The error text to display when the date in the field is invalid (defaults to
40796      * '{value} is not a valid date - it must be in the format {format}').
40797      */
40798     invalidText : "{0} is not a valid date - it must be in the format {1}",
40799     /**
40800      * @cfg {String} triggerClass
40801      * An additional CSS class used to style the trigger button.  The trigger will always get the
40802      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40803      * which displays a calendar icon).
40804      */
40805     triggerClass : 'x-form-date-trigger',
40806     
40807
40808     /**
40809      * @cfg {Boolean} useIso
40810      * if enabled, then the date field will use a hidden field to store the 
40811      * real value as iso formated date. default (true)
40812      */ 
40813     useIso : true,
40814     /**
40815      * @cfg {String/Object} autoCreate
40816      * A DomHelper element spec, or true for a default element spec (defaults to
40817      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40818      */ 
40819     // private
40820     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
40821     
40822     // private
40823     hiddenField: false,
40824     
40825     hideMonthPicker : false,
40826     
40827     onRender : function(ct, position)
40828     {
40829         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
40830         if (this.useIso) {
40831             this.el.dom.removeAttribute('name'); 
40832             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40833                     'before', true);
40834             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40835             // prevent input submission
40836             this.hiddenName = this.name;
40837         }
40838             
40839             
40840     },
40841     
40842     // private
40843     validateValue : function(value)
40844     {
40845         value = this.formatDate(value);
40846         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
40847             return false;
40848         }
40849         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40850              return true;
40851         }
40852         var svalue = value;
40853         value = this.parseDate(value);
40854         if(!value){
40855             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40856             return false;
40857         }
40858         var time = value.getTime();
40859         if(this.minValue && time < this.minValue.getTime()){
40860             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40861             return false;
40862         }
40863         if(this.maxValue && time > this.maxValue.getTime()){
40864             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40865             return false;
40866         }
40867         /*if(this.disabledDays){
40868             var day = value.getDay();
40869             for(var i = 0; i < this.disabledDays.length; i++) {
40870                 if(day === this.disabledDays[i]){
40871                     this.markInvalid(this.disabledDaysText);
40872                     return false;
40873                 }
40874             }
40875         }
40876         */
40877         var fvalue = this.formatDate(value);
40878         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
40879             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40880             return false;
40881         }
40882         */
40883         return true;
40884     },
40885
40886     // private
40887     // Provides logic to override the default TriggerField.validateBlur which just returns true
40888     validateBlur : function(){
40889         return !this.menu || !this.menu.isVisible();
40890     },
40891
40892     /**
40893      * Returns the current date value of the date field.
40894      * @return {Date} The date value
40895      */
40896     getValue : function(){
40897         
40898         
40899         
40900         return  this.hiddenField ?
40901                 this.hiddenField.value :
40902                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
40903     },
40904
40905     /**
40906      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40907      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
40908      * (the default format used is "m/d/y").
40909      * <br />Usage:
40910      * <pre><code>
40911 //All of these calls set the same date value (May 4, 2006)
40912
40913 //Pass a date object:
40914 var dt = new Date('5/4/06');
40915 monthField.setValue(dt);
40916
40917 //Pass a date string (default format):
40918 monthField.setValue('5/4/06');
40919
40920 //Pass a date string (custom format):
40921 monthField.format = 'Y-m-d';
40922 monthField.setValue('2006-5-4');
40923 </code></pre>
40924      * @param {String/Date} date The date or valid date string
40925      */
40926     setValue : function(date){
40927         Roo.log('month setValue' + date);
40928         // can only be first of month..
40929         
40930         var val = this.parseDate(date);
40931         
40932         if (this.hiddenField) {
40933             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40934         }
40935         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40936         this.value = this.parseDate(date);
40937     },
40938
40939     // private
40940     parseDate : function(value){
40941         if(!value || value instanceof Date){
40942             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
40943             return value;
40944         }
40945         var v = Date.parseDate(value, this.format);
40946         if (!v && this.useIso) {
40947             v = Date.parseDate(value, 'Y-m-d');
40948         }
40949         if (v) {
40950             // 
40951             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
40952         }
40953         
40954         
40955         if(!v && this.altFormats){
40956             if(!this.altFormatsArray){
40957                 this.altFormatsArray = this.altFormats.split("|");
40958             }
40959             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40960                 v = Date.parseDate(value, this.altFormatsArray[i]);
40961             }
40962         }
40963         return v;
40964     },
40965
40966     // private
40967     formatDate : function(date, fmt){
40968         return (!date || !(date instanceof Date)) ?
40969                date : date.dateFormat(fmt || this.format);
40970     },
40971
40972     // private
40973     menuListeners : {
40974         select: function(m, d){
40975             this.setValue(d);
40976             this.fireEvent('select', this, d);
40977         },
40978         show : function(){ // retain focus styling
40979             this.onFocus();
40980         },
40981         hide : function(){
40982             this.focus.defer(10, this);
40983             var ml = this.menuListeners;
40984             this.menu.un("select", ml.select,  this);
40985             this.menu.un("show", ml.show,  this);
40986             this.menu.un("hide", ml.hide,  this);
40987         }
40988     },
40989     // private
40990     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40991     onTriggerClick : function(){
40992         if(this.disabled){
40993             return;
40994         }
40995         if(this.menu == null){
40996             this.menu = new Roo.menu.DateMenu();
40997            
40998         }
40999         
41000         Roo.apply(this.menu.picker,  {
41001             
41002             showClear: this.allowBlank,
41003             minDate : this.minValue,
41004             maxDate : this.maxValue,
41005             disabledDatesRE : this.ddMatch,
41006             disabledDatesText : this.disabledDatesText,
41007             
41008             format : this.useIso ? 'Y-m-d' : this.format,
41009             minText : String.format(this.minText, this.formatDate(this.minValue)),
41010             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41011             
41012         });
41013          this.menu.on(Roo.apply({}, this.menuListeners, {
41014             scope:this
41015         }));
41016        
41017         
41018         var m = this.menu;
41019         var p = m.picker;
41020         
41021         // hide month picker get's called when we called by 'before hide';
41022         
41023         var ignorehide = true;
41024         p.hideMonthPicker  = function(disableAnim){
41025             if (ignorehide) {
41026                 return;
41027             }
41028              if(this.monthPicker){
41029                 Roo.log("hideMonthPicker called");
41030                 if(disableAnim === true){
41031                     this.monthPicker.hide();
41032                 }else{
41033                     this.monthPicker.slideOut('t', {duration:.2});
41034                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41035                     p.fireEvent("select", this, this.value);
41036                     m.hide();
41037                 }
41038             }
41039         }
41040         
41041         Roo.log('picker set value');
41042         Roo.log(this.getValue());
41043         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41044         m.show(this.el, 'tl-bl?');
41045         ignorehide  = false;
41046         // this will trigger hideMonthPicker..
41047         
41048         
41049         // hidden the day picker
41050         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41051         
41052         
41053         
41054       
41055         
41056         p.showMonthPicker.defer(100, p);
41057     
41058         
41059        
41060     },
41061
41062     beforeBlur : function(){
41063         var v = this.parseDate(this.getRawValue());
41064         if(v){
41065             this.setValue(v);
41066         }
41067     }
41068
41069     /** @cfg {Boolean} grow @hide */
41070     /** @cfg {Number} growMin @hide */
41071     /** @cfg {Number} growMax @hide */
41072     /**
41073      * @hide
41074      * @method autoSize
41075      */
41076 });/*
41077  * Based on:
41078  * Ext JS Library 1.1.1
41079  * Copyright(c) 2006-2007, Ext JS, LLC.
41080  *
41081  * Originally Released Under LGPL - original licence link has changed is not relivant.
41082  *
41083  * Fork - LGPL
41084  * <script type="text/javascript">
41085  */
41086  
41087
41088 /**
41089  * @class Roo.form.ComboBox
41090  * @extends Roo.form.TriggerField
41091  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41092  * @constructor
41093  * Create a new ComboBox.
41094  * @param {Object} config Configuration options
41095  */
41096 Roo.form.ComboBox = function(config){
41097     Roo.form.ComboBox.superclass.constructor.call(this, config);
41098     this.addEvents({
41099         /**
41100          * @event expand
41101          * Fires when the dropdown list is expanded
41102              * @param {Roo.form.ComboBox} combo This combo box
41103              */
41104         'expand' : true,
41105         /**
41106          * @event collapse
41107          * Fires when the dropdown list is collapsed
41108              * @param {Roo.form.ComboBox} combo This combo box
41109              */
41110         'collapse' : true,
41111         /**
41112          * @event beforeselect
41113          * Fires before a list item is selected. Return false to cancel the selection.
41114              * @param {Roo.form.ComboBox} combo This combo box
41115              * @param {Roo.data.Record} record The data record returned from the underlying store
41116              * @param {Number} index The index of the selected item in the dropdown list
41117              */
41118         'beforeselect' : true,
41119         /**
41120          * @event select
41121          * Fires when a list item is selected
41122              * @param {Roo.form.ComboBox} combo This combo box
41123              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41124              * @param {Number} index The index of the selected item in the dropdown list
41125              */
41126         'select' : true,
41127         /**
41128          * @event beforequery
41129          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41130          * The event object passed has these properties:
41131              * @param {Roo.form.ComboBox} combo This combo box
41132              * @param {String} query The query
41133              * @param {Boolean} forceAll true to force "all" query
41134              * @param {Boolean} cancel true to cancel the query
41135              * @param {Object} e The query event object
41136              */
41137         'beforequery': true,
41138          /**
41139          * @event add
41140          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41141              * @param {Roo.form.ComboBox} combo This combo box
41142              */
41143         'add' : true,
41144         /**
41145          * @event edit
41146          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41147              * @param {Roo.form.ComboBox} combo This combo box
41148              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41149              */
41150         'edit' : true
41151         
41152         
41153     });
41154     if(this.transform){
41155         this.allowDomMove = false;
41156         var s = Roo.getDom(this.transform);
41157         if(!this.hiddenName){
41158             this.hiddenName = s.name;
41159         }
41160         if(!this.store){
41161             this.mode = 'local';
41162             var d = [], opts = s.options;
41163             for(var i = 0, len = opts.length;i < len; i++){
41164                 var o = opts[i];
41165                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41166                 if(o.selected) {
41167                     this.value = value;
41168                 }
41169                 d.push([value, o.text]);
41170             }
41171             this.store = new Roo.data.SimpleStore({
41172                 'id': 0,
41173                 fields: ['value', 'text'],
41174                 data : d
41175             });
41176             this.valueField = 'value';
41177             this.displayField = 'text';
41178         }
41179         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41180         if(!this.lazyRender){
41181             this.target = true;
41182             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41183             s.parentNode.removeChild(s); // remove it
41184             this.render(this.el.parentNode);
41185         }else{
41186             s.parentNode.removeChild(s); // remove it
41187         }
41188
41189     }
41190     if (this.store) {
41191         this.store = Roo.factory(this.store, Roo.data);
41192     }
41193     
41194     this.selectedIndex = -1;
41195     if(this.mode == 'local'){
41196         if(config.queryDelay === undefined){
41197             this.queryDelay = 10;
41198         }
41199         if(config.minChars === undefined){
41200             this.minChars = 0;
41201         }
41202     }
41203 };
41204
41205 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41206     /**
41207      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41208      */
41209     /**
41210      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41211      * rendering into an Roo.Editor, defaults to false)
41212      */
41213     /**
41214      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41215      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41216      */
41217     /**
41218      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41219      */
41220     /**
41221      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41222      * the dropdown list (defaults to undefined, with no header element)
41223      */
41224
41225      /**
41226      * @cfg {String/Roo.Template} tpl The template to use to render the output
41227      */
41228      
41229     // private
41230     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41231     /**
41232      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41233      */
41234     listWidth: undefined,
41235     /**
41236      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41237      * mode = 'remote' or 'text' if mode = 'local')
41238      */
41239     displayField: undefined,
41240     /**
41241      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41242      * mode = 'remote' or 'value' if mode = 'local'). 
41243      * Note: use of a valueField requires the user make a selection
41244      * in order for a value to be mapped.
41245      */
41246     valueField: undefined,
41247     
41248     
41249     /**
41250      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41251      * field's data value (defaults to the underlying DOM element's name)
41252      */
41253     hiddenName: undefined,
41254     /**
41255      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41256      */
41257     listClass: '',
41258     /**
41259      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41260      */
41261     selectedClass: 'x-combo-selected',
41262     /**
41263      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41264      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41265      * which displays a downward arrow icon).
41266      */
41267     triggerClass : 'x-form-arrow-trigger',
41268     /**
41269      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41270      */
41271     shadow:'sides',
41272     /**
41273      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41274      * anchor positions (defaults to 'tl-bl')
41275      */
41276     listAlign: 'tl-bl?',
41277     /**
41278      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41279      */
41280     maxHeight: 300,
41281     /**
41282      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41283      * query specified by the allQuery config option (defaults to 'query')
41284      */
41285     triggerAction: 'query',
41286     /**
41287      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41288      * (defaults to 4, does not apply if editable = false)
41289      */
41290     minChars : 4,
41291     /**
41292      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41293      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41294      */
41295     typeAhead: false,
41296     /**
41297      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41298      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41299      */
41300     queryDelay: 500,
41301     /**
41302      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41303      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41304      */
41305     pageSize: 0,
41306     /**
41307      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41308      * when editable = true (defaults to false)
41309      */
41310     selectOnFocus:false,
41311     /**
41312      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41313      */
41314     queryParam: 'query',
41315     /**
41316      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41317      * when mode = 'remote' (defaults to 'Loading...')
41318      */
41319     loadingText: 'Loading...',
41320     /**
41321      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41322      */
41323     resizable: false,
41324     /**
41325      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41326      */
41327     handleHeight : 8,
41328     /**
41329      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41330      * traditional select (defaults to true)
41331      */
41332     editable: true,
41333     /**
41334      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41335      */
41336     allQuery: '',
41337     /**
41338      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41339      */
41340     mode: 'remote',
41341     /**
41342      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41343      * listWidth has a higher value)
41344      */
41345     minListWidth : 70,
41346     /**
41347      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41348      * allow the user to set arbitrary text into the field (defaults to false)
41349      */
41350     forceSelection:false,
41351     /**
41352      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41353      * if typeAhead = true (defaults to 250)
41354      */
41355     typeAheadDelay : 250,
41356     /**
41357      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41358      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41359      */
41360     valueNotFoundText : undefined,
41361     /**
41362      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41363      */
41364     blockFocus : false,
41365     
41366     /**
41367      * @cfg {Boolean} disableClear Disable showing of clear button.
41368      */
41369     disableClear : false,
41370     /**
41371      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41372      */
41373     alwaysQuery : false,
41374     
41375     //private
41376     addicon : false,
41377     editicon: false,
41378     
41379     // element that contains real text value.. (when hidden is used..)
41380      
41381     // private
41382     onRender : function(ct, position)
41383     {
41384         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41385         
41386         if(this.hiddenName){
41387             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41388                     'before', true);
41389             this.hiddenField.value =
41390                 this.hiddenValue !== undefined ? this.hiddenValue :
41391                 this.value !== undefined ? this.value : '';
41392
41393             // prevent input submission
41394             this.el.dom.removeAttribute('name');
41395              
41396              
41397         }
41398         
41399         if(Roo.isGecko){
41400             this.el.dom.setAttribute('autocomplete', 'off');
41401         }
41402
41403         var cls = 'x-combo-list';
41404
41405         this.list = new Roo.Layer({
41406             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41407         });
41408
41409         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41410         this.list.setWidth(lw);
41411         this.list.swallowEvent('mousewheel');
41412         this.assetHeight = 0;
41413
41414         if(this.title){
41415             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41416             this.assetHeight += this.header.getHeight();
41417         }
41418
41419         this.innerList = this.list.createChild({cls:cls+'-inner'});
41420         this.innerList.on('mouseover', this.onViewOver, this);
41421         this.innerList.on('mousemove', this.onViewMove, this);
41422         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41423         
41424         if(this.allowBlank && !this.pageSize && !this.disableClear){
41425             this.footer = this.list.createChild({cls:cls+'-ft'});
41426             this.pageTb = new Roo.Toolbar(this.footer);
41427            
41428         }
41429         if(this.pageSize){
41430             this.footer = this.list.createChild({cls:cls+'-ft'});
41431             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41432                     {pageSize: this.pageSize});
41433             
41434         }
41435         
41436         if (this.pageTb && this.allowBlank && !this.disableClear) {
41437             var _this = this;
41438             this.pageTb.add(new Roo.Toolbar.Fill(), {
41439                 cls: 'x-btn-icon x-btn-clear',
41440                 text: '&#160;',
41441                 handler: function()
41442                 {
41443                     _this.collapse();
41444                     _this.clearValue();
41445                     _this.onSelect(false, -1);
41446                 }
41447             });
41448         }
41449         if (this.footer) {
41450             this.assetHeight += this.footer.getHeight();
41451         }
41452         
41453
41454         if(!this.tpl){
41455             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41456         }
41457
41458         this.view = new Roo.View(this.innerList, this.tpl, {
41459             singleSelect:true,
41460             store: this.store,
41461             selectedClass: this.selectedClass
41462         });
41463
41464         this.view.on('click', this.onViewClick, this);
41465
41466         this.store.on('beforeload', this.onBeforeLoad, this);
41467         this.store.on('load', this.onLoad, this);
41468         this.store.on('loadexception', this.onLoadException, this);
41469
41470         if(this.resizable){
41471             this.resizer = new Roo.Resizable(this.list,  {
41472                pinned:true, handles:'se'
41473             });
41474             this.resizer.on('resize', function(r, w, h){
41475                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41476                 this.listWidth = w;
41477                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41478                 this.restrictHeight();
41479             }, this);
41480             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41481         }
41482         if(!this.editable){
41483             this.editable = true;
41484             this.setEditable(false);
41485         }  
41486         
41487         
41488         if (typeof(this.events.add.listeners) != 'undefined') {
41489             
41490             this.addicon = this.wrap.createChild(
41491                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41492        
41493             this.addicon.on('click', function(e) {
41494                 this.fireEvent('add', this);
41495             }, this);
41496         }
41497         if (typeof(this.events.edit.listeners) != 'undefined') {
41498             
41499             this.editicon = this.wrap.createChild(
41500                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41501             if (this.addicon) {
41502                 this.editicon.setStyle('margin-left', '40px');
41503             }
41504             this.editicon.on('click', function(e) {
41505                 
41506                 // we fire even  if inothing is selected..
41507                 this.fireEvent('edit', this, this.lastData );
41508                 
41509             }, this);
41510         }
41511         
41512         
41513         
41514     },
41515
41516     // private
41517     initEvents : function(){
41518         Roo.form.ComboBox.superclass.initEvents.call(this);
41519
41520         this.keyNav = new Roo.KeyNav(this.el, {
41521             "up" : function(e){
41522                 this.inKeyMode = true;
41523                 this.selectPrev();
41524             },
41525
41526             "down" : function(e){
41527                 if(!this.isExpanded()){
41528                     this.onTriggerClick();
41529                 }else{
41530                     this.inKeyMode = true;
41531                     this.selectNext();
41532                 }
41533             },
41534
41535             "enter" : function(e){
41536                 this.onViewClick();
41537                 //return true;
41538             },
41539
41540             "esc" : function(e){
41541                 this.collapse();
41542             },
41543
41544             "tab" : function(e){
41545                 this.onViewClick(false);
41546                 this.fireEvent("specialkey", this, e);
41547                 return true;
41548             },
41549
41550             scope : this,
41551
41552             doRelay : function(foo, bar, hname){
41553                 if(hname == 'down' || this.scope.isExpanded()){
41554                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41555                 }
41556                 return true;
41557             },
41558
41559             forceKeyDown: true
41560         });
41561         this.queryDelay = Math.max(this.queryDelay || 10,
41562                 this.mode == 'local' ? 10 : 250);
41563         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41564         if(this.typeAhead){
41565             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41566         }
41567         if(this.editable !== false){
41568             this.el.on("keyup", this.onKeyUp, this);
41569         }
41570         if(this.forceSelection){
41571             this.on('blur', this.doForce, this);
41572         }
41573     },
41574
41575     onDestroy : function(){
41576         if(this.view){
41577             this.view.setStore(null);
41578             this.view.el.removeAllListeners();
41579             this.view.el.remove();
41580             this.view.purgeListeners();
41581         }
41582         if(this.list){
41583             this.list.destroy();
41584         }
41585         if(this.store){
41586             this.store.un('beforeload', this.onBeforeLoad, this);
41587             this.store.un('load', this.onLoad, this);
41588             this.store.un('loadexception', this.onLoadException, this);
41589         }
41590         Roo.form.ComboBox.superclass.onDestroy.call(this);
41591     },
41592
41593     // private
41594     fireKey : function(e){
41595         if(e.isNavKeyPress() && !this.list.isVisible()){
41596             this.fireEvent("specialkey", this, e);
41597         }
41598     },
41599
41600     // private
41601     onResize: function(w, h){
41602         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41603         
41604         if(typeof w != 'number'){
41605             // we do not handle it!?!?
41606             return;
41607         }
41608         var tw = this.trigger.getWidth();
41609         tw += this.addicon ? this.addicon.getWidth() : 0;
41610         tw += this.editicon ? this.editicon.getWidth() : 0;
41611         var x = w - tw;
41612         this.el.setWidth( this.adjustWidth('input', x));
41613             
41614         this.trigger.setStyle('left', x+'px');
41615         
41616         if(this.list && this.listWidth === undefined){
41617             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41618             this.list.setWidth(lw);
41619             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41620         }
41621         
41622     
41623         
41624     },
41625
41626     /**
41627      * Allow or prevent the user from directly editing the field text.  If false is passed,
41628      * the user will only be able to select from the items defined in the dropdown list.  This method
41629      * is the runtime equivalent of setting the 'editable' config option at config time.
41630      * @param {Boolean} value True to allow the user to directly edit the field text
41631      */
41632     setEditable : function(value){
41633         if(value == this.editable){
41634             return;
41635         }
41636         this.editable = value;
41637         if(!value){
41638             this.el.dom.setAttribute('readOnly', true);
41639             this.el.on('mousedown', this.onTriggerClick,  this);
41640             this.el.addClass('x-combo-noedit');
41641         }else{
41642             this.el.dom.setAttribute('readOnly', false);
41643             this.el.un('mousedown', this.onTriggerClick,  this);
41644             this.el.removeClass('x-combo-noedit');
41645         }
41646     },
41647
41648     // private
41649     onBeforeLoad : function(){
41650         if(!this.hasFocus){
41651             return;
41652         }
41653         this.innerList.update(this.loadingText ?
41654                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41655         this.restrictHeight();
41656         this.selectedIndex = -1;
41657     },
41658
41659     // private
41660     onLoad : function(){
41661         if(!this.hasFocus){
41662             return;
41663         }
41664         if(this.store.getCount() > 0){
41665             this.expand();
41666             this.restrictHeight();
41667             if(this.lastQuery == this.allQuery){
41668                 if(this.editable){
41669                     this.el.dom.select();
41670                 }
41671                 if(!this.selectByValue(this.value, true)){
41672                     this.select(0, true);
41673                 }
41674             }else{
41675                 this.selectNext();
41676                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41677                     this.taTask.delay(this.typeAheadDelay);
41678                 }
41679             }
41680         }else{
41681             this.onEmptyResults();
41682         }
41683         //this.el.focus();
41684     },
41685     // private
41686     onLoadException : function()
41687     {
41688         this.collapse();
41689         Roo.log(this.store.reader.jsonData);
41690         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41691             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41692         }
41693         
41694         
41695     },
41696     // private
41697     onTypeAhead : function(){
41698         if(this.store.getCount() > 0){
41699             var r = this.store.getAt(0);
41700             var newValue = r.data[this.displayField];
41701             var len = newValue.length;
41702             var selStart = this.getRawValue().length;
41703             if(selStart != len){
41704                 this.setRawValue(newValue);
41705                 this.selectText(selStart, newValue.length);
41706             }
41707         }
41708     },
41709
41710     // private
41711     onSelect : function(record, index){
41712         if(this.fireEvent('beforeselect', this, record, index) !== false){
41713             this.setFromData(index > -1 ? record.data : false);
41714             this.collapse();
41715             this.fireEvent('select', this, record, index);
41716         }
41717     },
41718
41719     /**
41720      * Returns the currently selected field value or empty string if no value is set.
41721      * @return {String} value The selected value
41722      */
41723     getValue : function(){
41724         if(this.valueField){
41725             return typeof this.value != 'undefined' ? this.value : '';
41726         }
41727         return Roo.form.ComboBox.superclass.getValue.call(this);
41728     },
41729
41730     /**
41731      * Clears any text/value currently set in the field
41732      */
41733     clearValue : function(){
41734         if(this.hiddenField){
41735             this.hiddenField.value = '';
41736         }
41737         this.value = '';
41738         this.setRawValue('');
41739         this.lastSelectionText = '';
41740         
41741     },
41742
41743     /**
41744      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41745      * will be displayed in the field.  If the value does not match the data value of an existing item,
41746      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41747      * Otherwise the field will be blank (although the value will still be set).
41748      * @param {String} value The value to match
41749      */
41750     setValue : function(v){
41751         var text = v;
41752         if(this.valueField){
41753             var r = this.findRecord(this.valueField, v);
41754             if(r){
41755                 text = r.data[this.displayField];
41756             }else if(this.valueNotFoundText !== undefined){
41757                 text = this.valueNotFoundText;
41758             }
41759         }
41760         this.lastSelectionText = text;
41761         if(this.hiddenField){
41762             this.hiddenField.value = v;
41763         }
41764         Roo.form.ComboBox.superclass.setValue.call(this, text);
41765         this.value = v;
41766     },
41767     /**
41768      * @property {Object} the last set data for the element
41769      */
41770     
41771     lastData : false,
41772     /**
41773      * Sets the value of the field based on a object which is related to the record format for the store.
41774      * @param {Object} value the value to set as. or false on reset?
41775      */
41776     setFromData : function(o){
41777         var dv = ''; // display value
41778         var vv = ''; // value value..
41779         this.lastData = o;
41780         if (this.displayField) {
41781             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
41782         } else {
41783             // this is an error condition!!!
41784             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
41785         }
41786         
41787         if(this.valueField){
41788             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
41789         }
41790         if(this.hiddenField){
41791             this.hiddenField.value = vv;
41792             
41793             this.lastSelectionText = dv;
41794             Roo.form.ComboBox.superclass.setValue.call(this, dv);
41795             this.value = vv;
41796             return;
41797         }
41798         // no hidden field.. - we store the value in 'value', but still display
41799         // display field!!!!
41800         this.lastSelectionText = dv;
41801         Roo.form.ComboBox.superclass.setValue.call(this, dv);
41802         this.value = vv;
41803         
41804         
41805     },
41806     // private
41807     reset : function(){
41808         // overridden so that last data is reset..
41809         this.setValue(this.resetValue);
41810         this.originalValue = this.getValue();
41811         this.clearInvalid();
41812         this.lastData = false;
41813         if (this.view) {
41814             this.view.clearSelections();
41815         }
41816     },
41817     // private
41818     findRecord : function(prop, value){
41819         var record;
41820         if(this.store.getCount() > 0){
41821             this.store.each(function(r){
41822                 if(r.data[prop] == value){
41823                     record = r;
41824                     return false;
41825                 }
41826                 return true;
41827             });
41828         }
41829         return record;
41830     },
41831     
41832     getName: function()
41833     {
41834         // returns hidden if it's set..
41835         if (!this.rendered) {return ''};
41836         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41837         
41838     },
41839     // private
41840     onViewMove : function(e, t){
41841         this.inKeyMode = false;
41842     },
41843
41844     // private
41845     onViewOver : function(e, t){
41846         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
41847             return;
41848         }
41849         var item = this.view.findItemFromChild(t);
41850         if(item){
41851             var index = this.view.indexOf(item);
41852             this.select(index, false);
41853         }
41854     },
41855
41856     // private
41857     onViewClick : function(doFocus)
41858     {
41859         var index = this.view.getSelectedIndexes()[0];
41860         var r = this.store.getAt(index);
41861         if(r){
41862             this.onSelect(r, index);
41863         }
41864         if(doFocus !== false && !this.blockFocus){
41865             this.el.focus();
41866         }
41867     },
41868
41869     // private
41870     restrictHeight : function(){
41871         this.innerList.dom.style.height = '';
41872         var inner = this.innerList.dom;
41873         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
41874         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
41875         this.list.beginUpdate();
41876         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
41877         this.list.alignTo(this.el, this.listAlign);
41878         this.list.endUpdate();
41879     },
41880
41881     // private
41882     onEmptyResults : function(){
41883         this.collapse();
41884     },
41885
41886     /**
41887      * Returns true if the dropdown list is expanded, else false.
41888      */
41889     isExpanded : function(){
41890         return this.list.isVisible();
41891     },
41892
41893     /**
41894      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
41895      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41896      * @param {String} value The data value of the item to select
41897      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41898      * selected item if it is not currently in view (defaults to true)
41899      * @return {Boolean} True if the value matched an item in the list, else false
41900      */
41901     selectByValue : function(v, scrollIntoView){
41902         if(v !== undefined && v !== null){
41903             var r = this.findRecord(this.valueField || this.displayField, v);
41904             if(r){
41905                 this.select(this.store.indexOf(r), scrollIntoView);
41906                 return true;
41907             }
41908         }
41909         return false;
41910     },
41911
41912     /**
41913      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
41914      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
41915      * @param {Number} index The zero-based index of the list item to select
41916      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
41917      * selected item if it is not currently in view (defaults to true)
41918      */
41919     select : function(index, scrollIntoView){
41920         this.selectedIndex = index;
41921         this.view.select(index);
41922         if(scrollIntoView !== false){
41923             var el = this.view.getNode(index);
41924             if(el){
41925                 this.innerList.scrollChildIntoView(el, false);
41926             }
41927         }
41928     },
41929
41930     // private
41931     selectNext : function(){
41932         var ct = this.store.getCount();
41933         if(ct > 0){
41934             if(this.selectedIndex == -1){
41935                 this.select(0);
41936             }else if(this.selectedIndex < ct-1){
41937                 this.select(this.selectedIndex+1);
41938             }
41939         }
41940     },
41941
41942     // private
41943     selectPrev : function(){
41944         var ct = this.store.getCount();
41945         if(ct > 0){
41946             if(this.selectedIndex == -1){
41947                 this.select(0);
41948             }else if(this.selectedIndex != 0){
41949                 this.select(this.selectedIndex-1);
41950             }
41951         }
41952     },
41953
41954     // private
41955     onKeyUp : function(e){
41956         if(this.editable !== false && !e.isSpecialKey()){
41957             this.lastKey = e.getKey();
41958             this.dqTask.delay(this.queryDelay);
41959         }
41960     },
41961
41962     // private
41963     validateBlur : function(){
41964         return !this.list || !this.list.isVisible();   
41965     },
41966
41967     // private
41968     initQuery : function(){
41969         this.doQuery(this.getRawValue());
41970     },
41971
41972     // private
41973     doForce : function(){
41974         if(this.el.dom.value.length > 0){
41975             this.el.dom.value =
41976                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
41977              
41978         }
41979     },
41980
41981     /**
41982      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
41983      * query allowing the query action to be canceled if needed.
41984      * @param {String} query The SQL query to execute
41985      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
41986      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
41987      * saved in the current store (defaults to false)
41988      */
41989     doQuery : function(q, forceAll){
41990         if(q === undefined || q === null){
41991             q = '';
41992         }
41993         var qe = {
41994             query: q,
41995             forceAll: forceAll,
41996             combo: this,
41997             cancel:false
41998         };
41999         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42000             return false;
42001         }
42002         q = qe.query;
42003         forceAll = qe.forceAll;
42004         if(forceAll === true || (q.length >= this.minChars)){
42005             if(this.lastQuery != q || this.alwaysQuery){
42006                 this.lastQuery = q;
42007                 if(this.mode == 'local'){
42008                     this.selectedIndex = -1;
42009                     if(forceAll){
42010                         this.store.clearFilter();
42011                     }else{
42012                         this.store.filter(this.displayField, q);
42013                     }
42014                     this.onLoad();
42015                 }else{
42016                     this.store.baseParams[this.queryParam] = q;
42017                     this.store.load({
42018                         params: this.getParams(q)
42019                     });
42020                     this.expand();
42021                 }
42022             }else{
42023                 this.selectedIndex = -1;
42024                 this.onLoad();   
42025             }
42026         }
42027     },
42028
42029     // private
42030     getParams : function(q){
42031         var p = {};
42032         //p[this.queryParam] = q;
42033         if(this.pageSize){
42034             p.start = 0;
42035             p.limit = this.pageSize;
42036         }
42037         return p;
42038     },
42039
42040     /**
42041      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42042      */
42043     collapse : function(){
42044         if(!this.isExpanded()){
42045             return;
42046         }
42047         this.list.hide();
42048         Roo.get(document).un('mousedown', this.collapseIf, this);
42049         Roo.get(document).un('mousewheel', this.collapseIf, this);
42050         if (!this.editable) {
42051             Roo.get(document).un('keydown', this.listKeyPress, this);
42052         }
42053         this.fireEvent('collapse', this);
42054     },
42055
42056     // private
42057     collapseIf : function(e){
42058         if(!e.within(this.wrap) && !e.within(this.list)){
42059             this.collapse();
42060         }
42061     },
42062
42063     /**
42064      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42065      */
42066     expand : function(){
42067         if(this.isExpanded() || !this.hasFocus){
42068             return;
42069         }
42070         this.list.alignTo(this.el, this.listAlign);
42071         this.list.show();
42072         Roo.get(document).on('mousedown', this.collapseIf, this);
42073         Roo.get(document).on('mousewheel', this.collapseIf, this);
42074         if (!this.editable) {
42075             Roo.get(document).on('keydown', this.listKeyPress, this);
42076         }
42077         
42078         this.fireEvent('expand', this);
42079     },
42080
42081     // private
42082     // Implements the default empty TriggerField.onTriggerClick function
42083     onTriggerClick : function(){
42084         if(this.disabled){
42085             return;
42086         }
42087         if(this.isExpanded()){
42088             this.collapse();
42089             if (!this.blockFocus) {
42090                 this.el.focus();
42091             }
42092             
42093         }else {
42094             this.hasFocus = true;
42095             if(this.triggerAction == 'all') {
42096                 this.doQuery(this.allQuery, true);
42097             } else {
42098                 this.doQuery(this.getRawValue());
42099             }
42100             if (!this.blockFocus) {
42101                 this.el.focus();
42102             }
42103         }
42104     },
42105     listKeyPress : function(e)
42106     {
42107         //Roo.log('listkeypress');
42108         // scroll to first matching element based on key pres..
42109         if (e.isSpecialKey()) {
42110             return false;
42111         }
42112         var k = String.fromCharCode(e.getKey()).toUpperCase();
42113         //Roo.log(k);
42114         var match  = false;
42115         var csel = this.view.getSelectedNodes();
42116         var cselitem = false;
42117         if (csel.length) {
42118             var ix = this.view.indexOf(csel[0]);
42119             cselitem  = this.store.getAt(ix);
42120             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42121                 cselitem = false;
42122             }
42123             
42124         }
42125         
42126         this.store.each(function(v) { 
42127             if (cselitem) {
42128                 // start at existing selection.
42129                 if (cselitem.id == v.id) {
42130                     cselitem = false;
42131                 }
42132                 return;
42133             }
42134                 
42135             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42136                 match = this.store.indexOf(v);
42137                 return false;
42138             }
42139         }, this);
42140         
42141         if (match === false) {
42142             return true; // no more action?
42143         }
42144         // scroll to?
42145         this.view.select(match);
42146         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42147         sn.scrollIntoView(sn.dom.parentNode, false);
42148     } 
42149
42150     /** 
42151     * @cfg {Boolean} grow 
42152     * @hide 
42153     */
42154     /** 
42155     * @cfg {Number} growMin 
42156     * @hide 
42157     */
42158     /** 
42159     * @cfg {Number} growMax 
42160     * @hide 
42161     */
42162     /**
42163      * @hide
42164      * @method autoSize
42165      */
42166 });/*
42167  * Copyright(c) 2010-2012, Roo J Solutions Limited
42168  *
42169  * Licence LGPL
42170  *
42171  */
42172
42173 /**
42174  * @class Roo.form.ComboBoxArray
42175  * @extends Roo.form.TextField
42176  * A facebook style adder... for lists of email / people / countries  etc...
42177  * pick multiple items from a combo box, and shows each one.
42178  *
42179  *  Fred [x]  Brian [x]  [Pick another |v]
42180  *
42181  *
42182  *  For this to work: it needs various extra information
42183  *    - normal combo problay has
42184  *      name, hiddenName
42185  *    + displayField, valueField
42186  *
42187  *    For our purpose...
42188  *
42189  *
42190  *   If we change from 'extends' to wrapping...
42191  *   
42192  *  
42193  *
42194  
42195  
42196  * @constructor
42197  * Create a new ComboBoxArray.
42198  * @param {Object} config Configuration options
42199  */
42200  
42201
42202 Roo.form.ComboBoxArray = function(config)
42203 {
42204     this.addEvents({
42205         /**
42206          * @event beforeremove
42207          * Fires before remove the value from the list
42208              * @param {Roo.form.ComboBoxArray} _self This combo box array
42209              * @param {Roo.form.ComboBoxArray.Item} item removed item
42210              */
42211         'beforeremove' : true,
42212         /**
42213          * @event remove
42214          * Fires when remove the value from the list
42215              * @param {Roo.form.ComboBoxArray} _self This combo box array
42216              * @param {Roo.form.ComboBoxArray.Item} item removed item
42217              */
42218         'remove' : true
42219         
42220         
42221     });
42222     
42223     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42224     
42225     this.items = new Roo.util.MixedCollection(false);
42226     
42227     // construct the child combo...
42228     
42229     
42230     
42231     
42232    
42233     
42234 }
42235
42236  
42237 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42238
42239     /**
42240      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42241      */
42242     
42243     lastData : false,
42244     
42245     // behavies liek a hiddne field
42246     inputType:      'hidden',
42247     /**
42248      * @cfg {Number} width The width of the box that displays the selected element
42249      */ 
42250     width:          300,
42251
42252     
42253     
42254     /**
42255      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42256      */
42257     name : false,
42258     /**
42259      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42260      */
42261     hiddenName : false,
42262     
42263     
42264     // private the array of items that are displayed..
42265     items  : false,
42266     // private - the hidden field el.
42267     hiddenEl : false,
42268     // private - the filed el..
42269     el : false,
42270     
42271     //validateValue : function() { return true; }, // all values are ok!
42272     //onAddClick: function() { },
42273     
42274     onRender : function(ct, position) 
42275     {
42276         
42277         // create the standard hidden element
42278         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42279         
42280         
42281         // give fake names to child combo;
42282         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42283         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42284         
42285         this.combo = Roo.factory(this.combo, Roo.form);
42286         this.combo.onRender(ct, position);
42287         if (typeof(this.combo.width) != 'undefined') {
42288             this.combo.onResize(this.combo.width,0);
42289         }
42290         
42291         this.combo.initEvents();
42292         
42293         // assigned so form know we need to do this..
42294         this.store          = this.combo.store;
42295         this.valueField     = this.combo.valueField;
42296         this.displayField   = this.combo.displayField ;
42297         
42298         
42299         this.combo.wrap.addClass('x-cbarray-grp');
42300         
42301         var cbwrap = this.combo.wrap.createChild(
42302             {tag: 'div', cls: 'x-cbarray-cb'},
42303             this.combo.el.dom
42304         );
42305         
42306              
42307         this.hiddenEl = this.combo.wrap.createChild({
42308             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42309         });
42310         this.el = this.combo.wrap.createChild({
42311             tag: 'input',  type:'hidden' , name: this.name, value : ''
42312         });
42313          //   this.el.dom.removeAttribute("name");
42314         
42315         
42316         this.outerWrap = this.combo.wrap;
42317         this.wrap = cbwrap;
42318         
42319         this.outerWrap.setWidth(this.width);
42320         this.outerWrap.dom.removeChild(this.el.dom);
42321         
42322         this.wrap.dom.appendChild(this.el.dom);
42323         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42324         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42325         
42326         this.combo.trigger.setStyle('position','relative');
42327         this.combo.trigger.setStyle('left', '0px');
42328         this.combo.trigger.setStyle('top', '2px');
42329         
42330         this.combo.el.setStyle('vertical-align', 'text-bottom');
42331         
42332         //this.trigger.setStyle('vertical-align', 'top');
42333         
42334         // this should use the code from combo really... on('add' ....)
42335         if (this.adder) {
42336             
42337         
42338             this.adder = this.outerWrap.createChild(
42339                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42340             var _t = this;
42341             this.adder.on('click', function(e) {
42342                 _t.fireEvent('adderclick', this, e);
42343             }, _t);
42344         }
42345         //var _t = this;
42346         //this.adder.on('click', this.onAddClick, _t);
42347         
42348         
42349         this.combo.on('select', function(cb, rec, ix) {
42350             this.addItem(rec.data);
42351             
42352             cb.setValue('');
42353             cb.el.dom.value = '';
42354             //cb.lastData = rec.data;
42355             // add to list
42356             
42357         }, this);
42358         
42359         
42360     },
42361     
42362     
42363     getName: function()
42364     {
42365         // returns hidden if it's set..
42366         if (!this.rendered) {return ''};
42367         return  this.hiddenName ? this.hiddenName : this.name;
42368         
42369     },
42370     
42371     
42372     onResize: function(w, h){
42373         
42374         return;
42375         // not sure if this is needed..
42376         //this.combo.onResize(w,h);
42377         
42378         if(typeof w != 'number'){
42379             // we do not handle it!?!?
42380             return;
42381         }
42382         var tw = this.combo.trigger.getWidth();
42383         tw += this.addicon ? this.addicon.getWidth() : 0;
42384         tw += this.editicon ? this.editicon.getWidth() : 0;
42385         var x = w - tw;
42386         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42387             
42388         this.combo.trigger.setStyle('left', '0px');
42389         
42390         if(this.list && this.listWidth === undefined){
42391             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42392             this.list.setWidth(lw);
42393             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42394         }
42395         
42396     
42397         
42398     },
42399     
42400     addItem: function(rec)
42401     {
42402         var valueField = this.combo.valueField;
42403         var displayField = this.combo.displayField;
42404         
42405         if (this.items.indexOfKey(rec[valueField]) > -1) {
42406             //console.log("GOT " + rec.data.id);
42407             return;
42408         }
42409         
42410         var x = new Roo.form.ComboBoxArray.Item({
42411             //id : rec[this.idField],
42412             data : rec,
42413             displayField : displayField ,
42414             tipField : displayField ,
42415             cb : this
42416         });
42417         // use the 
42418         this.items.add(rec[valueField],x);
42419         // add it before the element..
42420         this.updateHiddenEl();
42421         x.render(this.outerWrap, this.wrap.dom);
42422         // add the image handler..
42423     },
42424     
42425     updateHiddenEl : function()
42426     {
42427         this.validate();
42428         if (!this.hiddenEl) {
42429             return;
42430         }
42431         var ar = [];
42432         var idField = this.combo.valueField;
42433         
42434         this.items.each(function(f) {
42435             ar.push(f.data[idField]);
42436         });
42437         this.hiddenEl.dom.value = ar.join(',');
42438         this.validate();
42439     },
42440     
42441     reset : function()
42442     {
42443         this.items.clear();
42444         
42445         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42446            el.remove();
42447         });
42448         
42449         this.el.dom.value = '';
42450         if (this.hiddenEl) {
42451             this.hiddenEl.dom.value = '';
42452         }
42453         
42454     },
42455     getValue: function()
42456     {
42457         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42458     },
42459     setValue: function(v) // not a valid action - must use addItems..
42460     {
42461         
42462         this.reset();
42463          
42464         if (this.store.isLocal && (typeof(v) == 'string')) {
42465             // then we can use the store to find the values..
42466             // comma seperated at present.. this needs to allow JSON based encoding..
42467             this.hiddenEl.value  = v;
42468             var v_ar = [];
42469             Roo.each(v.split(','), function(k) {
42470                 Roo.log("CHECK " + this.valueField + ',' + k);
42471                 var li = this.store.query(this.valueField, k);
42472                 if (!li.length) {
42473                     return;
42474                 }
42475                 var add = {};
42476                 add[this.valueField] = k;
42477                 add[this.displayField] = li.item(0).data[this.displayField];
42478                 
42479                 this.addItem(add);
42480             }, this) 
42481              
42482         }
42483         if (typeof(v) == 'object' ) {
42484             // then let's assume it's an array of objects..
42485             Roo.each(v, function(l) {
42486                 this.addItem(l);
42487             }, this);
42488              
42489         }
42490         
42491         
42492     },
42493     setFromData: function(v)
42494     {
42495         // this recieves an object, if setValues is called.
42496         this.reset();
42497         this.el.dom.value = v[this.displayField];
42498         this.hiddenEl.dom.value = v[this.valueField];
42499         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42500             return;
42501         }
42502         var kv = v[this.valueField];
42503         var dv = v[this.displayField];
42504         kv = typeof(kv) != 'string' ? '' : kv;
42505         dv = typeof(dv) != 'string' ? '' : dv;
42506         
42507         
42508         var keys = kv.split(',');
42509         var display = dv.split(',');
42510         for (var i = 0 ; i < keys.length; i++) {
42511             
42512             add = {};
42513             add[this.valueField] = keys[i];
42514             add[this.displayField] = display[i];
42515             this.addItem(add);
42516         }
42517       
42518         
42519     },
42520     
42521     /**
42522      * Validates the combox array value
42523      * @return {Boolean} True if the value is valid, else false
42524      */
42525     validate : function(){
42526         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42527             this.clearInvalid();
42528             return true;
42529         }
42530         return false;
42531     },
42532     
42533     validateValue : function(value){
42534         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42535         
42536     },
42537     
42538     /*@
42539      * overide
42540      * 
42541      */
42542     isDirty : function() {
42543         if(this.disabled) {
42544             return false;
42545         }
42546         
42547         try {
42548             var d = Roo.decode(String(this.originalValue));
42549         } catch (e) {
42550             return String(this.getValue()) !== String(this.originalValue);
42551         }
42552         
42553         var originalValue = [];
42554         
42555         for (var i = 0; i < d.length; i++){
42556             originalValue.push(d[i][this.valueField]);
42557         }
42558         
42559         return String(this.getValue()) !== String(originalValue.join(','));
42560         
42561     }
42562     
42563 });
42564
42565
42566
42567 /**
42568  * @class Roo.form.ComboBoxArray.Item
42569  * @extends Roo.BoxComponent
42570  * A selected item in the list
42571  *  Fred [x]  Brian [x]  [Pick another |v]
42572  * 
42573  * @constructor
42574  * Create a new item.
42575  * @param {Object} config Configuration options
42576  */
42577  
42578 Roo.form.ComboBoxArray.Item = function(config) {
42579     config.id = Roo.id();
42580     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42581 }
42582
42583 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42584     data : {},
42585     cb: false,
42586     displayField : false,
42587     tipField : false,
42588     
42589     
42590     defaultAutoCreate : {
42591         tag: 'div',
42592         cls: 'x-cbarray-item',
42593         cn : [ 
42594             { tag: 'div' },
42595             {
42596                 tag: 'img',
42597                 width:16,
42598                 height : 16,
42599                 src : Roo.BLANK_IMAGE_URL ,
42600                 align: 'center'
42601             }
42602         ]
42603         
42604     },
42605     
42606  
42607     onRender : function(ct, position)
42608     {
42609         Roo.form.Field.superclass.onRender.call(this, ct, position);
42610         
42611         if(!this.el){
42612             var cfg = this.getAutoCreate();
42613             this.el = ct.createChild(cfg, position);
42614         }
42615         
42616         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42617         
42618         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42619             this.cb.renderer(this.data) :
42620             String.format('{0}',this.data[this.displayField]);
42621         
42622             
42623         this.el.child('div').dom.setAttribute('qtip',
42624                         String.format('{0}',this.data[this.tipField])
42625         );
42626         
42627         this.el.child('img').on('click', this.remove, this);
42628         
42629     },
42630    
42631     remove : function()
42632     {
42633         if(this.cb.disabled){
42634             return;
42635         }
42636         
42637         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42638             this.cb.items.remove(this);
42639             this.el.child('img').un('click', this.remove, this);
42640             this.el.remove();
42641             this.cb.updateHiddenEl();
42642
42643             this.cb.fireEvent('remove', this.cb, this);
42644         }
42645         
42646     }
42647 });/*
42648  * RooJS Library 1.1.1
42649  * Copyright(c) 2008-2011  Alan Knowles
42650  *
42651  * License - LGPL
42652  */
42653  
42654
42655 /**
42656  * @class Roo.form.ComboNested
42657  * @extends Roo.form.ComboBox
42658  * A combobox for that allows selection of nested items in a list,
42659  * eg.
42660  *
42661  *  Book
42662  *    -> red
42663  *    -> green
42664  *  Table
42665  *    -> square
42666  *      ->red
42667  *      ->green
42668  *    -> rectangle
42669  *      ->green
42670  *      
42671  * 
42672  * @constructor
42673  * Create a new ComboNested
42674  * @param {Object} config Configuration options
42675  */
42676 Roo.form.ComboNested = function(config){
42677     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42678     // should verify some data...
42679     // like
42680     // hiddenName = required..
42681     // displayField = required
42682     // valudField == required
42683     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42684     var _t = this;
42685     Roo.each(req, function(e) {
42686         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42687             throw "Roo.form.ComboNested : missing value for: " + e;
42688         }
42689     });
42690      
42691     
42692 };
42693
42694 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42695    
42696    
42697     list : null, // the outermost div..
42698     innerLists : null, // the
42699     views : null,
42700     stores : null,
42701     // private
42702     onRender : function(ct, position)
42703     {
42704         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42705         
42706         if(this.hiddenName){
42707             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42708                     'before', true);
42709             this.hiddenField.value =
42710                 this.hiddenValue !== undefined ? this.hiddenValue :
42711                 this.value !== undefined ? this.value : '';
42712
42713             // prevent input submission
42714             this.el.dom.removeAttribute('name');
42715              
42716              
42717         }
42718         
42719         if(Roo.isGecko){
42720             this.el.dom.setAttribute('autocomplete', 'off');
42721         }
42722
42723         var cls = 'x-combo-list';
42724
42725         this.list = new Roo.Layer({
42726             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42727         });
42728
42729         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42730         this.list.setWidth(lw);
42731         this.list.swallowEvent('mousewheel');
42732         this.assetHeight = 0;
42733
42734         if(this.title){
42735             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42736             this.assetHeight += this.header.getHeight();
42737         }
42738         this.innerLists = [];
42739         this.views = [];
42740         this.stores = [];
42741         for (var i =0 ; i < 3; i++) {
42742             this.onRenderList( cls, i);
42743         }
42744         
42745         // always needs footer, as we are going to have an 'OK' button.
42746         this.footer = this.list.createChild({cls:cls+'-ft'});
42747         this.pageTb = new Roo.Toolbar(this.footer);  
42748         var _this = this;
42749         this.pageTb.add(  {
42750             
42751             text: 'Done',
42752             handler: function()
42753             {
42754                 _this.collapse();
42755             }
42756         });
42757         
42758         if ( this.allowBlank && !this.disableClear) {
42759             
42760             this.pageTb.add(new Roo.Toolbar.Fill(), {
42761                 cls: 'x-btn-icon x-btn-clear',
42762                 text: '&#160;',
42763                 handler: function()
42764                 {
42765                     _this.collapse();
42766                     _this.clearValue();
42767                     _this.onSelect(false, -1);
42768                 }
42769             });
42770         }
42771         if (this.footer) {
42772             this.assetHeight += this.footer.getHeight();
42773         }
42774         
42775     },
42776     onRenderList : function (  cls, i)
42777     {
42778         
42779         var lw = Math.floor(
42780                 ((this.listWidth * 3 || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / 3
42781         );
42782         
42783         this.list.setWidth(lw); // default to '1'
42784
42785         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
42786         //il.on('mouseover', this.onViewOver, this, { list:  i });
42787         //il.on('mousemove', this.onViewMove, this, { list:  i });
42788         il.setWidth(lw);
42789         il.setStyle({ 'overflow-x' : 'hidden'});
42790
42791         if(!this.tpl){
42792             this.tpl = new Roo.Template({
42793                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
42794                 isEmpty: function (value, allValues) {
42795                     return value.length ? 'has-children' : 'no-children'
42796                 }
42797             });
42798         }
42799         
42800         var store  = this.store;
42801         if (i > 0) {
42802             store  = new Roo.data.SimpleStore({
42803                 reader : this.store.reader,
42804                 data : [ ]
42805             });
42806         }
42807         this.stores[i]  = store;
42808                 
42809         
42810         
42811         var view = this.views[i] = new Roo.View(
42812             il,
42813             this.tpl,
42814             {
42815                 singleSelect:true,
42816                 store: store,
42817                 selectedClass: this.selectedClass
42818             }
42819         );
42820         view.getEl().setWidth(lw);
42821         view.getEl().setStyle({
42822             position: i < 1 ? 'relative' : 'absolute',
42823             top: 0,
42824             left: (i * lw ) + 'px',
42825             display : i > 0 ? 'none' : 'block'
42826         });
42827         view.on('selectionchange', this.onSelectChange, this, {list : i });
42828         view.on('dblclick', this.onDoubleClick, this, {list : i });
42829         //view.on('click', this.onViewClick, this, { list : i });
42830
42831         store.on('beforeload', this.onBeforeLoad, this);
42832         store.on('load',  this.onStoreLoad, this, { list  : i});
42833         store.on('loadexception', this.onLoadException, this);
42834
42835         // hide the other vies..
42836         
42837         
42838         
42839     },
42840     onResize : function()  {},
42841     
42842     restrictHeight : function()
42843     {
42844         var mh = 0;
42845         Roo.each(this.innerLists, function(il,i) {
42846             var el = this.views[i].getEl();
42847             el.dom.style.height = '';
42848             var inner = el.dom;
42849             var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42850             // only adjust heights on other ones..
42851             if (i < 1) {
42852                 
42853                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42854                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42855                 mh = Math.max(el.getHeight(), mh);
42856             }
42857             
42858             
42859         }, this);
42860         
42861         this.list.beginUpdate();
42862         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
42863         this.list.alignTo(this.el, this.listAlign);
42864         this.list.endUpdate();
42865         
42866     },
42867      
42868     
42869     // -- store handlers..
42870     
42871     // private
42872     onLoad : function(a,b,c,d)
42873     {
42874         
42875         if(!this.hasFocus){
42876             return;
42877         }
42878         
42879         if(this.store.getCount() > 0) {
42880             this.expand();
42881             this.restrictHeight();   
42882         } else {
42883             this.onEmptyResults();
42884         }
42885         /*
42886         this.stores[1].loadData([]);
42887         this.stores[2].loadData([]);
42888         this.views
42889         */    
42890     
42891         //this.el.focus();
42892     },
42893     onStoreLoad : function ()
42894     {
42895         Roo.log(arguments);
42896     },
42897     
42898     // private
42899     onLoadException : function()
42900     {
42901         this.collapse();
42902         Roo.log(this.store.reader.jsonData);
42903         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42904             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42905         }
42906         
42907         
42908     } ,
42909      
42910      
42911
42912     onSelectChange : function (view, sels, opts )
42913     {
42914         var ix = view.getSelectedIndexes();
42915         
42916         
42917         if (opts.list > 1) {
42918              
42919             this.setFromData(ix.length ? view.store.getAt(ix[0]).data : {});
42920             return;
42921         }
42922         
42923         if (!ix.length) {
42924             this.setFromData({});
42925             this.stores[opts.list+1].loadData( [] );
42926             return;
42927         }
42928         
42929         var rec = view.store.getAt(ix[0]);
42930         this.setFromData(rec.data);
42931         
42932         var lw = Math.floor(
42933                 ((this.listWidth * 3 || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / 3
42934         );
42935         
42936         this.stores[opts.list+1].loadData( typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn);
42937         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
42938         this.views[opts.list+1].getEl().setStyle({ display : rec.data.cn.length ? 'block' : 'none' });
42939         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
42940         this.list.setWidth(lw * (opts.list + (rec.data.cn.length ? 2 : 1))); 
42941     },
42942     onDoubleClick : function()
42943     {
42944         this.collapse(); //??
42945     },
42946     
42947      
42948     
42949     findRecord : function (prop,value)
42950     {
42951         return this.findRecordInStore(this.store, prop,value);
42952     },
42953     
42954      // private
42955     findRecordInStore : function(store, prop, value)
42956     {
42957         var cstore = new Roo.data.SimpleStore({
42958             reader : this.store.reader,
42959             data : [ ]
42960         });
42961         var _this = this;
42962         var record  = false;
42963         if(store.getCount() > 0){
42964            store.each(function(r){
42965                 if(r.data[prop] == value){
42966                     record = r;
42967                     return false;
42968                 }
42969                 if (r.data.cn && r.data.cn.length) {
42970                     cstore.loadData( r.data.cn);
42971                     var cret = _this.findRecordInStore(cstore, prop, value);
42972                     if (cret !== false) {
42973                         record = cret;
42974                         return false;
42975                     }
42976                 }
42977                 
42978                 return true;
42979             });
42980         }
42981         return record;
42982     }
42983     
42984 });/*
42985  * Based on:
42986  * Ext JS Library 1.1.1
42987  * Copyright(c) 2006-2007, Ext JS, LLC.
42988  *
42989  * Originally Released Under LGPL - original licence link has changed is not relivant.
42990  *
42991  * Fork - LGPL
42992  * <script type="text/javascript">
42993  */
42994 /**
42995  * @class Roo.form.Checkbox
42996  * @extends Roo.form.Field
42997  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
42998  * @constructor
42999  * Creates a new Checkbox
43000  * @param {Object} config Configuration options
43001  */
43002 Roo.form.Checkbox = function(config){
43003     Roo.form.Checkbox.superclass.constructor.call(this, config);
43004     this.addEvents({
43005         /**
43006          * @event check
43007          * Fires when the checkbox is checked or unchecked.
43008              * @param {Roo.form.Checkbox} this This checkbox
43009              * @param {Boolean} checked The new checked value
43010              */
43011         check : true
43012     });
43013 };
43014
43015 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43016     /**
43017      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43018      */
43019     focusClass : undefined,
43020     /**
43021      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43022      */
43023     fieldClass: "x-form-field",
43024     /**
43025      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43026      */
43027     checked: false,
43028     /**
43029      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43030      * {tag: "input", type: "checkbox", autocomplete: "off"})
43031      */
43032     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43033     /**
43034      * @cfg {String} boxLabel The text that appears beside the checkbox
43035      */
43036     boxLabel : "",
43037     /**
43038      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43039      */  
43040     inputValue : '1',
43041     /**
43042      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43043      */
43044      valueOff: '0', // value when not checked..
43045
43046     actionMode : 'viewEl', 
43047     //
43048     // private
43049     itemCls : 'x-menu-check-item x-form-item',
43050     groupClass : 'x-menu-group-item',
43051     inputType : 'hidden',
43052     
43053     
43054     inSetChecked: false, // check that we are not calling self...
43055     
43056     inputElement: false, // real input element?
43057     basedOn: false, // ????
43058     
43059     isFormField: true, // not sure where this is needed!!!!
43060
43061     onResize : function(){
43062         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43063         if(!this.boxLabel){
43064             this.el.alignTo(this.wrap, 'c-c');
43065         }
43066     },
43067
43068     initEvents : function(){
43069         Roo.form.Checkbox.superclass.initEvents.call(this);
43070         this.el.on("click", this.onClick,  this);
43071         this.el.on("change", this.onClick,  this);
43072     },
43073
43074
43075     getResizeEl : function(){
43076         return this.wrap;
43077     },
43078
43079     getPositionEl : function(){
43080         return this.wrap;
43081     },
43082
43083     // private
43084     onRender : function(ct, position){
43085         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43086         /*
43087         if(this.inputValue !== undefined){
43088             this.el.dom.value = this.inputValue;
43089         }
43090         */
43091         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43092         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43093         var viewEl = this.wrap.createChild({ 
43094             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43095         this.viewEl = viewEl;   
43096         this.wrap.on('click', this.onClick,  this); 
43097         
43098         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43099         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43100         
43101         
43102         
43103         if(this.boxLabel){
43104             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43105         //    viewEl.on('click', this.onClick,  this); 
43106         }
43107         //if(this.checked){
43108             this.setChecked(this.checked);
43109         //}else{
43110             //this.checked = this.el.dom;
43111         //}
43112
43113     },
43114
43115     // private
43116     initValue : Roo.emptyFn,
43117
43118     /**
43119      * Returns the checked state of the checkbox.
43120      * @return {Boolean} True if checked, else false
43121      */
43122     getValue : function(){
43123         if(this.el){
43124             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43125         }
43126         return this.valueOff;
43127         
43128     },
43129
43130         // private
43131     onClick : function(){ 
43132         if (this.disabled) {
43133             return;
43134         }
43135         this.setChecked(!this.checked);
43136
43137         //if(this.el.dom.checked != this.checked){
43138         //    this.setValue(this.el.dom.checked);
43139        // }
43140     },
43141
43142     /**
43143      * Sets the checked state of the checkbox.
43144      * On is always based on a string comparison between inputValue and the param.
43145      * @param {Boolean/String} value - the value to set 
43146      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43147      */
43148     setValue : function(v,suppressEvent){
43149         
43150         
43151         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43152         //if(this.el && this.el.dom){
43153         //    this.el.dom.checked = this.checked;
43154         //    this.el.dom.defaultChecked = this.checked;
43155         //}
43156         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43157         //this.fireEvent("check", this, this.checked);
43158     },
43159     // private..
43160     setChecked : function(state,suppressEvent)
43161     {
43162         if (this.inSetChecked) {
43163             this.checked = state;
43164             return;
43165         }
43166         
43167     
43168         if(this.wrap){
43169             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43170         }
43171         this.checked = state;
43172         if(suppressEvent !== true){
43173             this.fireEvent('check', this, state);
43174         }
43175         this.inSetChecked = true;
43176         this.el.dom.value = state ? this.inputValue : this.valueOff;
43177         this.inSetChecked = false;
43178         
43179     },
43180     // handle setting of hidden value by some other method!!?!?
43181     setFromHidden: function()
43182     {
43183         if(!this.el){
43184             return;
43185         }
43186         //console.log("SET FROM HIDDEN");
43187         //alert('setFrom hidden');
43188         this.setValue(this.el.dom.value);
43189     },
43190     
43191     onDestroy : function()
43192     {
43193         if(this.viewEl){
43194             Roo.get(this.viewEl).remove();
43195         }
43196          
43197         Roo.form.Checkbox.superclass.onDestroy.call(this);
43198     },
43199     
43200     setBoxLabel : function(str)
43201     {
43202         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43203     }
43204
43205 });/*
43206  * Based on:
43207  * Ext JS Library 1.1.1
43208  * Copyright(c) 2006-2007, Ext JS, LLC.
43209  *
43210  * Originally Released Under LGPL - original licence link has changed is not relivant.
43211  *
43212  * Fork - LGPL
43213  * <script type="text/javascript">
43214  */
43215  
43216 /**
43217  * @class Roo.form.Radio
43218  * @extends Roo.form.Checkbox
43219  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43220  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43221  * @constructor
43222  * Creates a new Radio
43223  * @param {Object} config Configuration options
43224  */
43225 Roo.form.Radio = function(){
43226     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43227 };
43228 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43229     inputType: 'radio',
43230
43231     /**
43232      * If this radio is part of a group, it will return the selected value
43233      * @return {String}
43234      */
43235     getGroupValue : function(){
43236         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43237     },
43238     
43239     
43240     onRender : function(ct, position){
43241         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43242         
43243         if(this.inputValue !== undefined){
43244             this.el.dom.value = this.inputValue;
43245         }
43246          
43247         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43248         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43249         //var viewEl = this.wrap.createChild({ 
43250         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43251         //this.viewEl = viewEl;   
43252         //this.wrap.on('click', this.onClick,  this); 
43253         
43254         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43255         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43256         
43257         
43258         
43259         if(this.boxLabel){
43260             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43261         //    viewEl.on('click', this.onClick,  this); 
43262         }
43263          if(this.checked){
43264             this.el.dom.checked =   'checked' ;
43265         }
43266          
43267     } 
43268     
43269     
43270 });//<script type="text/javascript">
43271
43272 /*
43273  * Based  Ext JS Library 1.1.1
43274  * Copyright(c) 2006-2007, Ext JS, LLC.
43275  * LGPL
43276  *
43277  */
43278  
43279 /**
43280  * @class Roo.HtmlEditorCore
43281  * @extends Roo.Component
43282  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43283  *
43284  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43285  */
43286
43287 Roo.HtmlEditorCore = function(config){
43288     
43289     
43290     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43291     
43292     
43293     this.addEvents({
43294         /**
43295          * @event initialize
43296          * Fires when the editor is fully initialized (including the iframe)
43297          * @param {Roo.HtmlEditorCore} this
43298          */
43299         initialize: true,
43300         /**
43301          * @event activate
43302          * Fires when the editor is first receives the focus. Any insertion must wait
43303          * until after this event.
43304          * @param {Roo.HtmlEditorCore} this
43305          */
43306         activate: true,
43307          /**
43308          * @event beforesync
43309          * Fires before the textarea is updated with content from the editor iframe. Return false
43310          * to cancel the sync.
43311          * @param {Roo.HtmlEditorCore} this
43312          * @param {String} html
43313          */
43314         beforesync: true,
43315          /**
43316          * @event beforepush
43317          * Fires before the iframe editor is updated with content from the textarea. Return false
43318          * to cancel the push.
43319          * @param {Roo.HtmlEditorCore} this
43320          * @param {String} html
43321          */
43322         beforepush: true,
43323          /**
43324          * @event sync
43325          * Fires when the textarea is updated with content from the editor iframe.
43326          * @param {Roo.HtmlEditorCore} this
43327          * @param {String} html
43328          */
43329         sync: true,
43330          /**
43331          * @event push
43332          * Fires when the iframe editor is updated with content from the textarea.
43333          * @param {Roo.HtmlEditorCore} this
43334          * @param {String} html
43335          */
43336         push: true,
43337         
43338         /**
43339          * @event editorevent
43340          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43341          * @param {Roo.HtmlEditorCore} this
43342          */
43343         editorevent: true
43344         
43345     });
43346     
43347     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43348     
43349     // defaults : white / black...
43350     this.applyBlacklists();
43351     
43352     
43353     
43354 };
43355
43356
43357 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43358
43359
43360      /**
43361      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43362      */
43363     
43364     owner : false,
43365     
43366      /**
43367      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43368      *                        Roo.resizable.
43369      */
43370     resizable : false,
43371      /**
43372      * @cfg {Number} height (in pixels)
43373      */   
43374     height: 300,
43375    /**
43376      * @cfg {Number} width (in pixels)
43377      */   
43378     width: 500,
43379     
43380     /**
43381      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43382      * 
43383      */
43384     stylesheets: false,
43385     
43386     // id of frame..
43387     frameId: false,
43388     
43389     // private properties
43390     validationEvent : false,
43391     deferHeight: true,
43392     initialized : false,
43393     activated : false,
43394     sourceEditMode : false,
43395     onFocus : Roo.emptyFn,
43396     iframePad:3,
43397     hideMode:'offsets',
43398     
43399     clearUp: true,
43400     
43401     // blacklist + whitelisted elements..
43402     black: false,
43403     white: false,
43404      
43405     bodyCls : '',
43406
43407     /**
43408      * Protected method that will not generally be called directly. It
43409      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43410      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43411      */
43412     getDocMarkup : function(){
43413         // body styles..
43414         var st = '';
43415         
43416         // inherit styels from page...?? 
43417         if (this.stylesheets === false) {
43418             
43419             Roo.get(document.head).select('style').each(function(node) {
43420                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43421             });
43422             
43423             Roo.get(document.head).select('link').each(function(node) { 
43424                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43425             });
43426             
43427         } else if (!this.stylesheets.length) {
43428                 // simple..
43429                 st = '<style type="text/css">' +
43430                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43431                    '</style>';
43432         } else { 
43433             st = '<style type="text/css">' +
43434                     this.stylesheets +
43435                 '</style>';
43436         }
43437         
43438         st +=  '<style type="text/css">' +
43439             'IMG { cursor: pointer } ' +
43440         '</style>';
43441
43442         var cls = 'roo-htmleditor-body';
43443         
43444         if(this.bodyCls.length){
43445             cls += ' ' + this.bodyCls;
43446         }
43447         
43448         return '<html><head>' + st  +
43449             //<style type="text/css">' +
43450             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43451             //'</style>' +
43452             ' </head><body class="' +  cls + '"></body></html>';
43453     },
43454
43455     // private
43456     onRender : function(ct, position)
43457     {
43458         var _t = this;
43459         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43460         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43461         
43462         
43463         this.el.dom.style.border = '0 none';
43464         this.el.dom.setAttribute('tabIndex', -1);
43465         this.el.addClass('x-hidden hide');
43466         
43467         
43468         
43469         if(Roo.isIE){ // fix IE 1px bogus margin
43470             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43471         }
43472        
43473         
43474         this.frameId = Roo.id();
43475         
43476          
43477         
43478         var iframe = this.owner.wrap.createChild({
43479             tag: 'iframe',
43480             cls: 'form-control', // bootstrap..
43481             id: this.frameId,
43482             name: this.frameId,
43483             frameBorder : 'no',
43484             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43485         }, this.el
43486         );
43487         
43488         
43489         this.iframe = iframe.dom;
43490
43491          this.assignDocWin();
43492         
43493         this.doc.designMode = 'on';
43494        
43495         this.doc.open();
43496         this.doc.write(this.getDocMarkup());
43497         this.doc.close();
43498
43499         
43500         var task = { // must defer to wait for browser to be ready
43501             run : function(){
43502                 //console.log("run task?" + this.doc.readyState);
43503                 this.assignDocWin();
43504                 if(this.doc.body || this.doc.readyState == 'complete'){
43505                     try {
43506                         this.doc.designMode="on";
43507                     } catch (e) {
43508                         return;
43509                     }
43510                     Roo.TaskMgr.stop(task);
43511                     this.initEditor.defer(10, this);
43512                 }
43513             },
43514             interval : 10,
43515             duration: 10000,
43516             scope: this
43517         };
43518         Roo.TaskMgr.start(task);
43519
43520     },
43521
43522     // private
43523     onResize : function(w, h)
43524     {
43525          Roo.log('resize: ' +w + ',' + h );
43526         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43527         if(!this.iframe){
43528             return;
43529         }
43530         if(typeof w == 'number'){
43531             
43532             this.iframe.style.width = w + 'px';
43533         }
43534         if(typeof h == 'number'){
43535             
43536             this.iframe.style.height = h + 'px';
43537             if(this.doc){
43538                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43539             }
43540         }
43541         
43542     },
43543
43544     /**
43545      * Toggles the editor between standard and source edit mode.
43546      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43547      */
43548     toggleSourceEdit : function(sourceEditMode){
43549         
43550         this.sourceEditMode = sourceEditMode === true;
43551         
43552         if(this.sourceEditMode){
43553  
43554             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43555             
43556         }else{
43557             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43558             //this.iframe.className = '';
43559             this.deferFocus();
43560         }
43561         //this.setSize(this.owner.wrap.getSize());
43562         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43563     },
43564
43565     
43566   
43567
43568     /**
43569      * Protected method that will not generally be called directly. If you need/want
43570      * custom HTML cleanup, this is the method you should override.
43571      * @param {String} html The HTML to be cleaned
43572      * return {String} The cleaned HTML
43573      */
43574     cleanHtml : function(html){
43575         html = String(html);
43576         if(html.length > 5){
43577             if(Roo.isSafari){ // strip safari nonsense
43578                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43579             }
43580         }
43581         if(html == '&nbsp;'){
43582             html = '';
43583         }
43584         return html;
43585     },
43586
43587     /**
43588      * HTML Editor -> Textarea
43589      * Protected method that will not generally be called directly. Syncs the contents
43590      * of the editor iframe with the textarea.
43591      */
43592     syncValue : function(){
43593         if(this.initialized){
43594             var bd = (this.doc.body || this.doc.documentElement);
43595             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43596             var html = bd.innerHTML;
43597             if(Roo.isSafari){
43598                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43599                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43600                 if(m && m[1]){
43601                     html = '<div style="'+m[0]+'">' + html + '</div>';
43602                 }
43603             }
43604             html = this.cleanHtml(html);
43605             // fix up the special chars.. normaly like back quotes in word...
43606             // however we do not want to do this with chinese..
43607             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43608                 
43609                 var cc = match.charCodeAt();
43610
43611                 // Get the character value, handling surrogate pairs
43612                 if (match.length == 2) {
43613                     // It's a surrogate pair, calculate the Unicode code point
43614                     var high = match.charCodeAt(0) - 0xD800;
43615                     var low  = match.charCodeAt(1) - 0xDC00;
43616                     cc = (high * 0x400) + low + 0x10000;
43617                 }  else if (
43618                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43619                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43620                     (cc >= 0xf900 && cc < 0xfb00 )
43621                 ) {
43622                         return match;
43623                 }  
43624          
43625                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43626                 return "&#" + cc + ";";
43627                 
43628                 
43629             });
43630             
43631             
43632              
43633             if(this.owner.fireEvent('beforesync', this, html) !== false){
43634                 this.el.dom.value = html;
43635                 this.owner.fireEvent('sync', this, html);
43636             }
43637         }
43638     },
43639
43640     /**
43641      * Protected method that will not generally be called directly. Pushes the value of the textarea
43642      * into the iframe editor.
43643      */
43644     pushValue : function(){
43645         if(this.initialized){
43646             var v = this.el.dom.value.trim();
43647             
43648 //            if(v.length < 1){
43649 //                v = '&#160;';
43650 //            }
43651             
43652             if(this.owner.fireEvent('beforepush', this, v) !== false){
43653                 var d = (this.doc.body || this.doc.documentElement);
43654                 d.innerHTML = v;
43655                 this.cleanUpPaste();
43656                 this.el.dom.value = d.innerHTML;
43657                 this.owner.fireEvent('push', this, v);
43658             }
43659         }
43660     },
43661
43662     // private
43663     deferFocus : function(){
43664         this.focus.defer(10, this);
43665     },
43666
43667     // doc'ed in Field
43668     focus : function(){
43669         if(this.win && !this.sourceEditMode){
43670             this.win.focus();
43671         }else{
43672             this.el.focus();
43673         }
43674     },
43675     
43676     assignDocWin: function()
43677     {
43678         var iframe = this.iframe;
43679         
43680          if(Roo.isIE){
43681             this.doc = iframe.contentWindow.document;
43682             this.win = iframe.contentWindow;
43683         } else {
43684 //            if (!Roo.get(this.frameId)) {
43685 //                return;
43686 //            }
43687 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43688 //            this.win = Roo.get(this.frameId).dom.contentWindow;
43689             
43690             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
43691                 return;
43692             }
43693             
43694             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
43695             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
43696         }
43697     },
43698     
43699     // private
43700     initEditor : function(){
43701         //console.log("INIT EDITOR");
43702         this.assignDocWin();
43703         
43704         
43705         
43706         this.doc.designMode="on";
43707         this.doc.open();
43708         this.doc.write(this.getDocMarkup());
43709         this.doc.close();
43710         
43711         var dbody = (this.doc.body || this.doc.documentElement);
43712         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
43713         // this copies styles from the containing element into thsi one..
43714         // not sure why we need all of this..
43715         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
43716         
43717         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
43718         //ss['background-attachment'] = 'fixed'; // w3c
43719         dbody.bgProperties = 'fixed'; // ie
43720         //Roo.DomHelper.applyStyles(dbody, ss);
43721         Roo.EventManager.on(this.doc, {
43722             //'mousedown': this.onEditorEvent,
43723             'mouseup': this.onEditorEvent,
43724             'dblclick': this.onEditorEvent,
43725             'click': this.onEditorEvent,
43726             'keyup': this.onEditorEvent,
43727             buffer:100,
43728             scope: this
43729         });
43730         if(Roo.isGecko){
43731             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
43732         }
43733         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
43734             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
43735         }
43736         this.initialized = true;
43737
43738         this.owner.fireEvent('initialize', this);
43739         this.pushValue();
43740     },
43741
43742     // private
43743     onDestroy : function(){
43744         
43745         
43746         
43747         if(this.rendered){
43748             
43749             //for (var i =0; i < this.toolbars.length;i++) {
43750             //    // fixme - ask toolbars for heights?
43751             //    this.toolbars[i].onDestroy();
43752            // }
43753             
43754             //this.wrap.dom.innerHTML = '';
43755             //this.wrap.remove();
43756         }
43757     },
43758
43759     // private
43760     onFirstFocus : function(){
43761         
43762         this.assignDocWin();
43763         
43764         
43765         this.activated = true;
43766          
43767     
43768         if(Roo.isGecko){ // prevent silly gecko errors
43769             this.win.focus();
43770             var s = this.win.getSelection();
43771             if(!s.focusNode || s.focusNode.nodeType != 3){
43772                 var r = s.getRangeAt(0);
43773                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
43774                 r.collapse(true);
43775                 this.deferFocus();
43776             }
43777             try{
43778                 this.execCmd('useCSS', true);
43779                 this.execCmd('styleWithCSS', false);
43780             }catch(e){}
43781         }
43782         this.owner.fireEvent('activate', this);
43783     },
43784
43785     // private
43786     adjustFont: function(btn){
43787         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
43788         //if(Roo.isSafari){ // safari
43789         //    adjust *= 2;
43790        // }
43791         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
43792         if(Roo.isSafari){ // safari
43793             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
43794             v =  (v < 10) ? 10 : v;
43795             v =  (v > 48) ? 48 : v;
43796             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
43797             
43798         }
43799         
43800         
43801         v = Math.max(1, v+adjust);
43802         
43803         this.execCmd('FontSize', v  );
43804     },
43805
43806     onEditorEvent : function(e)
43807     {
43808         this.owner.fireEvent('editorevent', this, e);
43809       //  this.updateToolbar();
43810         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
43811     },
43812
43813     insertTag : function(tg)
43814     {
43815         // could be a bit smarter... -> wrap the current selected tRoo..
43816         if (tg.toLowerCase() == 'span' ||
43817             tg.toLowerCase() == 'code' ||
43818             tg.toLowerCase() == 'sup' ||
43819             tg.toLowerCase() == 'sub' 
43820             ) {
43821             
43822             range = this.createRange(this.getSelection());
43823             var wrappingNode = this.doc.createElement(tg.toLowerCase());
43824             wrappingNode.appendChild(range.extractContents());
43825             range.insertNode(wrappingNode);
43826
43827             return;
43828             
43829             
43830             
43831         }
43832         this.execCmd("formatblock",   tg);
43833         
43834     },
43835     
43836     insertText : function(txt)
43837     {
43838         
43839         
43840         var range = this.createRange();
43841         range.deleteContents();
43842                //alert(Sender.getAttribute('label'));
43843                
43844         range.insertNode(this.doc.createTextNode(txt));
43845     } ,
43846     
43847      
43848
43849     /**
43850      * Executes a Midas editor command on the editor document and performs necessary focus and
43851      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
43852      * @param {String} cmd The Midas command
43853      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43854      */
43855     relayCmd : function(cmd, value){
43856         this.win.focus();
43857         this.execCmd(cmd, value);
43858         this.owner.fireEvent('editorevent', this);
43859         //this.updateToolbar();
43860         this.owner.deferFocus();
43861     },
43862
43863     /**
43864      * Executes a Midas editor command directly on the editor document.
43865      * For visual commands, you should use {@link #relayCmd} instead.
43866      * <b>This should only be called after the editor is initialized.</b>
43867      * @param {String} cmd The Midas command
43868      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
43869      */
43870     execCmd : function(cmd, value){
43871         this.doc.execCommand(cmd, false, value === undefined ? null : value);
43872         this.syncValue();
43873     },
43874  
43875  
43876    
43877     /**
43878      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
43879      * to insert tRoo.
43880      * @param {String} text | dom node.. 
43881      */
43882     insertAtCursor : function(text)
43883     {
43884         
43885         if(!this.activated){
43886             return;
43887         }
43888         /*
43889         if(Roo.isIE){
43890             this.win.focus();
43891             var r = this.doc.selection.createRange();
43892             if(r){
43893                 r.collapse(true);
43894                 r.pasteHTML(text);
43895                 this.syncValue();
43896                 this.deferFocus();
43897             
43898             }
43899             return;
43900         }
43901         */
43902         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
43903             this.win.focus();
43904             
43905             
43906             // from jquery ui (MIT licenced)
43907             var range, node;
43908             var win = this.win;
43909             
43910             if (win.getSelection && win.getSelection().getRangeAt) {
43911                 range = win.getSelection().getRangeAt(0);
43912                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
43913                 range.insertNode(node);
43914             } else if (win.document.selection && win.document.selection.createRange) {
43915                 // no firefox support
43916                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43917                 win.document.selection.createRange().pasteHTML(txt);
43918             } else {
43919                 // no firefox support
43920                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
43921                 this.execCmd('InsertHTML', txt);
43922             } 
43923             
43924             this.syncValue();
43925             
43926             this.deferFocus();
43927         }
43928     },
43929  // private
43930     mozKeyPress : function(e){
43931         if(e.ctrlKey){
43932             var c = e.getCharCode(), cmd;
43933           
43934             if(c > 0){
43935                 c = String.fromCharCode(c).toLowerCase();
43936                 switch(c){
43937                     case 'b':
43938                         cmd = 'bold';
43939                         break;
43940                     case 'i':
43941                         cmd = 'italic';
43942                         break;
43943                     
43944                     case 'u':
43945                         cmd = 'underline';
43946                         break;
43947                     
43948                     case 'v':
43949                         this.cleanUpPaste.defer(100, this);
43950                         return;
43951                         
43952                 }
43953                 if(cmd){
43954                     this.win.focus();
43955                     this.execCmd(cmd);
43956                     this.deferFocus();
43957                     e.preventDefault();
43958                 }
43959                 
43960             }
43961         }
43962     },
43963
43964     // private
43965     fixKeys : function(){ // load time branching for fastest keydown performance
43966         if(Roo.isIE){
43967             return function(e){
43968                 var k = e.getKey(), r;
43969                 if(k == e.TAB){
43970                     e.stopEvent();
43971                     r = this.doc.selection.createRange();
43972                     if(r){
43973                         r.collapse(true);
43974                         r.pasteHTML('&#160;&#160;&#160;&#160;');
43975                         this.deferFocus();
43976                     }
43977                     return;
43978                 }
43979                 
43980                 if(k == e.ENTER){
43981                     r = this.doc.selection.createRange();
43982                     if(r){
43983                         var target = r.parentElement();
43984                         if(!target || target.tagName.toLowerCase() != 'li'){
43985                             e.stopEvent();
43986                             r.pasteHTML('<br />');
43987                             r.collapse(false);
43988                             r.select();
43989                         }
43990                     }
43991                 }
43992                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
43993                     this.cleanUpPaste.defer(100, this);
43994                     return;
43995                 }
43996                 
43997                 
43998             };
43999         }else if(Roo.isOpera){
44000             return function(e){
44001                 var k = e.getKey();
44002                 if(k == e.TAB){
44003                     e.stopEvent();
44004                     this.win.focus();
44005                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44006                     this.deferFocus();
44007                 }
44008                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44009                     this.cleanUpPaste.defer(100, this);
44010                     return;
44011                 }
44012                 
44013             };
44014         }else if(Roo.isSafari){
44015             return function(e){
44016                 var k = e.getKey();
44017                 
44018                 if(k == e.TAB){
44019                     e.stopEvent();
44020                     this.execCmd('InsertText','\t');
44021                     this.deferFocus();
44022                     return;
44023                 }
44024                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44025                     this.cleanUpPaste.defer(100, this);
44026                     return;
44027                 }
44028                 
44029              };
44030         }
44031     }(),
44032     
44033     getAllAncestors: function()
44034     {
44035         var p = this.getSelectedNode();
44036         var a = [];
44037         if (!p) {
44038             a.push(p); // push blank onto stack..
44039             p = this.getParentElement();
44040         }
44041         
44042         
44043         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44044             a.push(p);
44045             p = p.parentNode;
44046         }
44047         a.push(this.doc.body);
44048         return a;
44049     },
44050     lastSel : false,
44051     lastSelNode : false,
44052     
44053     
44054     getSelection : function() 
44055     {
44056         this.assignDocWin();
44057         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44058     },
44059     
44060     getSelectedNode: function() 
44061     {
44062         // this may only work on Gecko!!!
44063         
44064         // should we cache this!!!!
44065         
44066         
44067         
44068          
44069         var range = this.createRange(this.getSelection()).cloneRange();
44070         
44071         if (Roo.isIE) {
44072             var parent = range.parentElement();
44073             while (true) {
44074                 var testRange = range.duplicate();
44075                 testRange.moveToElementText(parent);
44076                 if (testRange.inRange(range)) {
44077                     break;
44078                 }
44079                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44080                     break;
44081                 }
44082                 parent = parent.parentElement;
44083             }
44084             return parent;
44085         }
44086         
44087         // is ancestor a text element.
44088         var ac =  range.commonAncestorContainer;
44089         if (ac.nodeType == 3) {
44090             ac = ac.parentNode;
44091         }
44092         
44093         var ar = ac.childNodes;
44094          
44095         var nodes = [];
44096         var other_nodes = [];
44097         var has_other_nodes = false;
44098         for (var i=0;i<ar.length;i++) {
44099             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44100                 continue;
44101             }
44102             // fullly contained node.
44103             
44104             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44105                 nodes.push(ar[i]);
44106                 continue;
44107             }
44108             
44109             // probably selected..
44110             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44111                 other_nodes.push(ar[i]);
44112                 continue;
44113             }
44114             // outer..
44115             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44116                 continue;
44117             }
44118             
44119             
44120             has_other_nodes = true;
44121         }
44122         if (!nodes.length && other_nodes.length) {
44123             nodes= other_nodes;
44124         }
44125         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44126             return false;
44127         }
44128         
44129         return nodes[0];
44130     },
44131     createRange: function(sel)
44132     {
44133         // this has strange effects when using with 
44134         // top toolbar - not sure if it's a great idea.
44135         //this.editor.contentWindow.focus();
44136         if (typeof sel != "undefined") {
44137             try {
44138                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44139             } catch(e) {
44140                 return this.doc.createRange();
44141             }
44142         } else {
44143             return this.doc.createRange();
44144         }
44145     },
44146     getParentElement: function()
44147     {
44148         
44149         this.assignDocWin();
44150         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44151         
44152         var range = this.createRange(sel);
44153          
44154         try {
44155             var p = range.commonAncestorContainer;
44156             while (p.nodeType == 3) { // text node
44157                 p = p.parentNode;
44158             }
44159             return p;
44160         } catch (e) {
44161             return null;
44162         }
44163     
44164     },
44165     /***
44166      *
44167      * Range intersection.. the hard stuff...
44168      *  '-1' = before
44169      *  '0' = hits..
44170      *  '1' = after.
44171      *         [ -- selected range --- ]
44172      *   [fail]                        [fail]
44173      *
44174      *    basically..
44175      *      if end is before start or  hits it. fail.
44176      *      if start is after end or hits it fail.
44177      *
44178      *   if either hits (but other is outside. - then it's not 
44179      *   
44180      *    
44181      **/
44182     
44183     
44184     // @see http://www.thismuchiknow.co.uk/?p=64.
44185     rangeIntersectsNode : function(range, node)
44186     {
44187         var nodeRange = node.ownerDocument.createRange();
44188         try {
44189             nodeRange.selectNode(node);
44190         } catch (e) {
44191             nodeRange.selectNodeContents(node);
44192         }
44193     
44194         var rangeStartRange = range.cloneRange();
44195         rangeStartRange.collapse(true);
44196     
44197         var rangeEndRange = range.cloneRange();
44198         rangeEndRange.collapse(false);
44199     
44200         var nodeStartRange = nodeRange.cloneRange();
44201         nodeStartRange.collapse(true);
44202     
44203         var nodeEndRange = nodeRange.cloneRange();
44204         nodeEndRange.collapse(false);
44205     
44206         return rangeStartRange.compareBoundaryPoints(
44207                  Range.START_TO_START, nodeEndRange) == -1 &&
44208                rangeEndRange.compareBoundaryPoints(
44209                  Range.START_TO_START, nodeStartRange) == 1;
44210         
44211          
44212     },
44213     rangeCompareNode : function(range, node)
44214     {
44215         var nodeRange = node.ownerDocument.createRange();
44216         try {
44217             nodeRange.selectNode(node);
44218         } catch (e) {
44219             nodeRange.selectNodeContents(node);
44220         }
44221         
44222         
44223         range.collapse(true);
44224     
44225         nodeRange.collapse(true);
44226      
44227         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44228         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44229          
44230         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44231         
44232         var nodeIsBefore   =  ss == 1;
44233         var nodeIsAfter    = ee == -1;
44234         
44235         if (nodeIsBefore && nodeIsAfter) {
44236             return 0; // outer
44237         }
44238         if (!nodeIsBefore && nodeIsAfter) {
44239             return 1; //right trailed.
44240         }
44241         
44242         if (nodeIsBefore && !nodeIsAfter) {
44243             return 2;  // left trailed.
44244         }
44245         // fully contined.
44246         return 3;
44247     },
44248
44249     // private? - in a new class?
44250     cleanUpPaste :  function()
44251     {
44252         // cleans up the whole document..
44253         Roo.log('cleanuppaste');
44254         
44255         this.cleanUpChildren(this.doc.body);
44256         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44257         if (clean != this.doc.body.innerHTML) {
44258             this.doc.body.innerHTML = clean;
44259         }
44260         
44261     },
44262     
44263     cleanWordChars : function(input) {// change the chars to hex code
44264         var he = Roo.HtmlEditorCore;
44265         
44266         var output = input;
44267         Roo.each(he.swapCodes, function(sw) { 
44268             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44269             
44270             output = output.replace(swapper, sw[1]);
44271         });
44272         
44273         return output;
44274     },
44275     
44276     
44277     cleanUpChildren : function (n)
44278     {
44279         if (!n.childNodes.length) {
44280             return;
44281         }
44282         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44283            this.cleanUpChild(n.childNodes[i]);
44284         }
44285     },
44286     
44287     
44288         
44289     
44290     cleanUpChild : function (node)
44291     {
44292         var ed = this;
44293         //console.log(node);
44294         if (node.nodeName == "#text") {
44295             // clean up silly Windows -- stuff?
44296             return; 
44297         }
44298         if (node.nodeName == "#comment") {
44299             node.parentNode.removeChild(node);
44300             // clean up silly Windows -- stuff?
44301             return; 
44302         }
44303         var lcname = node.tagName.toLowerCase();
44304         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44305         // whitelist of tags..
44306         
44307         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44308             // remove node.
44309             node.parentNode.removeChild(node);
44310             return;
44311             
44312         }
44313         
44314         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44315         
44316         // spans with no attributes - just remove them..
44317         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44318             remove_keep_children = true;
44319         }
44320         
44321         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44322         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44323         
44324         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44325         //    remove_keep_children = true;
44326         //}
44327         
44328         if (remove_keep_children) {
44329             this.cleanUpChildren(node);
44330             // inserts everything just before this node...
44331             while (node.childNodes.length) {
44332                 var cn = node.childNodes[0];
44333                 node.removeChild(cn);
44334                 node.parentNode.insertBefore(cn, node);
44335             }
44336             node.parentNode.removeChild(node);
44337             return;
44338         }
44339         
44340         if (!node.attributes || !node.attributes.length) {
44341             
44342           
44343             
44344             
44345             this.cleanUpChildren(node);
44346             return;
44347         }
44348         
44349         function cleanAttr(n,v)
44350         {
44351             
44352             if (v.match(/^\./) || v.match(/^\//)) {
44353                 return;
44354             }
44355             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44356                 return;
44357             }
44358             if (v.match(/^#/)) {
44359                 return;
44360             }
44361 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44362             node.removeAttribute(n);
44363             
44364         }
44365         
44366         var cwhite = this.cwhite;
44367         var cblack = this.cblack;
44368             
44369         function cleanStyle(n,v)
44370         {
44371             if (v.match(/expression/)) { //XSS?? should we even bother..
44372                 node.removeAttribute(n);
44373                 return;
44374             }
44375             
44376             var parts = v.split(/;/);
44377             var clean = [];
44378             
44379             Roo.each(parts, function(p) {
44380                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44381                 if (!p.length) {
44382                     return true;
44383                 }
44384                 var l = p.split(':').shift().replace(/\s+/g,'');
44385                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44386                 
44387                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44388 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44389                     //node.removeAttribute(n);
44390                     return true;
44391                 }
44392                 //Roo.log()
44393                 // only allow 'c whitelisted system attributes'
44394                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44395 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44396                     //node.removeAttribute(n);
44397                     return true;
44398                 }
44399                 
44400                 
44401                  
44402                 
44403                 clean.push(p);
44404                 return true;
44405             });
44406             if (clean.length) { 
44407                 node.setAttribute(n, clean.join(';'));
44408             } else {
44409                 node.removeAttribute(n);
44410             }
44411             
44412         }
44413         
44414         
44415         for (var i = node.attributes.length-1; i > -1 ; i--) {
44416             var a = node.attributes[i];
44417             //console.log(a);
44418             
44419             if (a.name.toLowerCase().substr(0,2)=='on')  {
44420                 node.removeAttribute(a.name);
44421                 continue;
44422             }
44423             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44424                 node.removeAttribute(a.name);
44425                 continue;
44426             }
44427             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44428                 cleanAttr(a.name,a.value); // fixme..
44429                 continue;
44430             }
44431             if (a.name == 'style') {
44432                 cleanStyle(a.name,a.value);
44433                 continue;
44434             }
44435             /// clean up MS crap..
44436             // tecnically this should be a list of valid class'es..
44437             
44438             
44439             if (a.name == 'class') {
44440                 if (a.value.match(/^Mso/)) {
44441                     node.removeAttribute('class');
44442                 }
44443                 
44444                 if (a.value.match(/^body$/)) {
44445                     node.removeAttribute('class');
44446                 }
44447                 continue;
44448             }
44449             
44450             // style cleanup!?
44451             // class cleanup?
44452             
44453         }
44454         
44455         
44456         this.cleanUpChildren(node);
44457         
44458         
44459     },
44460     
44461     /**
44462      * Clean up MS wordisms...
44463      */
44464     cleanWord : function(node)
44465     {
44466         if (!node) {
44467             this.cleanWord(this.doc.body);
44468             return;
44469         }
44470         
44471         if(
44472                 node.nodeName == 'SPAN' &&
44473                 !node.hasAttributes() &&
44474                 node.childNodes.length == 1 &&
44475                 node.firstChild.nodeName == "#text"  
44476         ) {
44477             var textNode = node.firstChild;
44478             node.removeChild(textNode);
44479             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44480                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44481             }
44482             node.parentNode.insertBefore(textNode, node);
44483             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44484                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44485             }
44486             node.parentNode.removeChild(node);
44487         }
44488         
44489         if (node.nodeName == "#text") {
44490             // clean up silly Windows -- stuff?
44491             return; 
44492         }
44493         if (node.nodeName == "#comment") {
44494             node.parentNode.removeChild(node);
44495             // clean up silly Windows -- stuff?
44496             return; 
44497         }
44498         
44499         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44500             node.parentNode.removeChild(node);
44501             return;
44502         }
44503         //Roo.log(node.tagName);
44504         // remove - but keep children..
44505         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44506             //Roo.log('-- removed');
44507             while (node.childNodes.length) {
44508                 var cn = node.childNodes[0];
44509                 node.removeChild(cn);
44510                 node.parentNode.insertBefore(cn, node);
44511                 // move node to parent - and clean it..
44512                 this.cleanWord(cn);
44513             }
44514             node.parentNode.removeChild(node);
44515             /// no need to iterate chidlren = it's got none..
44516             //this.iterateChildren(node, this.cleanWord);
44517             return;
44518         }
44519         // clean styles
44520         if (node.className.length) {
44521             
44522             var cn = node.className.split(/\W+/);
44523             var cna = [];
44524             Roo.each(cn, function(cls) {
44525                 if (cls.match(/Mso[a-zA-Z]+/)) {
44526                     return;
44527                 }
44528                 cna.push(cls);
44529             });
44530             node.className = cna.length ? cna.join(' ') : '';
44531             if (!cna.length) {
44532                 node.removeAttribute("class");
44533             }
44534         }
44535         
44536         if (node.hasAttribute("lang")) {
44537             node.removeAttribute("lang");
44538         }
44539         
44540         if (node.hasAttribute("style")) {
44541             
44542             var styles = node.getAttribute("style").split(";");
44543             var nstyle = [];
44544             Roo.each(styles, function(s) {
44545                 if (!s.match(/:/)) {
44546                     return;
44547                 }
44548                 var kv = s.split(":");
44549                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44550                     return;
44551                 }
44552                 // what ever is left... we allow.
44553                 nstyle.push(s);
44554             });
44555             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44556             if (!nstyle.length) {
44557                 node.removeAttribute('style');
44558             }
44559         }
44560         this.iterateChildren(node, this.cleanWord);
44561         
44562         
44563         
44564     },
44565     /**
44566      * iterateChildren of a Node, calling fn each time, using this as the scole..
44567      * @param {DomNode} node node to iterate children of.
44568      * @param {Function} fn method of this class to call on each item.
44569      */
44570     iterateChildren : function(node, fn)
44571     {
44572         if (!node.childNodes.length) {
44573                 return;
44574         }
44575         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44576            fn.call(this, node.childNodes[i])
44577         }
44578     },
44579     
44580     
44581     /**
44582      * cleanTableWidths.
44583      *
44584      * Quite often pasting from word etc.. results in tables with column and widths.
44585      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44586      *
44587      */
44588     cleanTableWidths : function(node)
44589     {
44590          
44591          
44592         if (!node) {
44593             this.cleanTableWidths(this.doc.body);
44594             return;
44595         }
44596         
44597         // ignore list...
44598         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44599             return; 
44600         }
44601         Roo.log(node.tagName);
44602         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44603             this.iterateChildren(node, this.cleanTableWidths);
44604             return;
44605         }
44606         if (node.hasAttribute('width')) {
44607             node.removeAttribute('width');
44608         }
44609         
44610          
44611         if (node.hasAttribute("style")) {
44612             // pretty basic...
44613             
44614             var styles = node.getAttribute("style").split(";");
44615             var nstyle = [];
44616             Roo.each(styles, function(s) {
44617                 if (!s.match(/:/)) {
44618                     return;
44619                 }
44620                 var kv = s.split(":");
44621                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44622                     return;
44623                 }
44624                 // what ever is left... we allow.
44625                 nstyle.push(s);
44626             });
44627             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44628             if (!nstyle.length) {
44629                 node.removeAttribute('style');
44630             }
44631         }
44632         
44633         this.iterateChildren(node, this.cleanTableWidths);
44634         
44635         
44636     },
44637     
44638     
44639     
44640     
44641     domToHTML : function(currentElement, depth, nopadtext) {
44642         
44643         depth = depth || 0;
44644         nopadtext = nopadtext || false;
44645     
44646         if (!currentElement) {
44647             return this.domToHTML(this.doc.body);
44648         }
44649         
44650         //Roo.log(currentElement);
44651         var j;
44652         var allText = false;
44653         var nodeName = currentElement.nodeName;
44654         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44655         
44656         if  (nodeName == '#text') {
44657             
44658             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44659         }
44660         
44661         
44662         var ret = '';
44663         if (nodeName != 'BODY') {
44664              
44665             var i = 0;
44666             // Prints the node tagName, such as <A>, <IMG>, etc
44667             if (tagName) {
44668                 var attr = [];
44669                 for(i = 0; i < currentElement.attributes.length;i++) {
44670                     // quoting?
44671                     var aname = currentElement.attributes.item(i).name;
44672                     if (!currentElement.attributes.item(i).value.length) {
44673                         continue;
44674                     }
44675                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
44676                 }
44677                 
44678                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
44679             } 
44680             else {
44681                 
44682                 // eack
44683             }
44684         } else {
44685             tagName = false;
44686         }
44687         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
44688             return ret;
44689         }
44690         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
44691             nopadtext = true;
44692         }
44693         
44694         
44695         // Traverse the tree
44696         i = 0;
44697         var currentElementChild = currentElement.childNodes.item(i);
44698         var allText = true;
44699         var innerHTML  = '';
44700         lastnode = '';
44701         while (currentElementChild) {
44702             // Formatting code (indent the tree so it looks nice on the screen)
44703             var nopad = nopadtext;
44704             if (lastnode == 'SPAN') {
44705                 nopad  = true;
44706             }
44707             // text
44708             if  (currentElementChild.nodeName == '#text') {
44709                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
44710                 toadd = nopadtext ? toadd : toadd.trim();
44711                 if (!nopad && toadd.length > 80) {
44712                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
44713                 }
44714                 innerHTML  += toadd;
44715                 
44716                 i++;
44717                 currentElementChild = currentElement.childNodes.item(i);
44718                 lastNode = '';
44719                 continue;
44720             }
44721             allText = false;
44722             
44723             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
44724                 
44725             // Recursively traverse the tree structure of the child node
44726             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
44727             lastnode = currentElementChild.nodeName;
44728             i++;
44729             currentElementChild=currentElement.childNodes.item(i);
44730         }
44731         
44732         ret += innerHTML;
44733         
44734         if (!allText) {
44735                 // The remaining code is mostly for formatting the tree
44736             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
44737         }
44738         
44739         
44740         if (tagName) {
44741             ret+= "</"+tagName+">";
44742         }
44743         return ret;
44744         
44745     },
44746         
44747     applyBlacklists : function()
44748     {
44749         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
44750         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
44751         
44752         this.white = [];
44753         this.black = [];
44754         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
44755             if (b.indexOf(tag) > -1) {
44756                 return;
44757             }
44758             this.white.push(tag);
44759             
44760         }, this);
44761         
44762         Roo.each(w, function(tag) {
44763             if (b.indexOf(tag) > -1) {
44764                 return;
44765             }
44766             if (this.white.indexOf(tag) > -1) {
44767                 return;
44768             }
44769             this.white.push(tag);
44770             
44771         }, this);
44772         
44773         
44774         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
44775             if (w.indexOf(tag) > -1) {
44776                 return;
44777             }
44778             this.black.push(tag);
44779             
44780         }, this);
44781         
44782         Roo.each(b, function(tag) {
44783             if (w.indexOf(tag) > -1) {
44784                 return;
44785             }
44786             if (this.black.indexOf(tag) > -1) {
44787                 return;
44788             }
44789             this.black.push(tag);
44790             
44791         }, this);
44792         
44793         
44794         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
44795         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
44796         
44797         this.cwhite = [];
44798         this.cblack = [];
44799         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
44800             if (b.indexOf(tag) > -1) {
44801                 return;
44802             }
44803             this.cwhite.push(tag);
44804             
44805         }, this);
44806         
44807         Roo.each(w, function(tag) {
44808             if (b.indexOf(tag) > -1) {
44809                 return;
44810             }
44811             if (this.cwhite.indexOf(tag) > -1) {
44812                 return;
44813             }
44814             this.cwhite.push(tag);
44815             
44816         }, this);
44817         
44818         
44819         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
44820             if (w.indexOf(tag) > -1) {
44821                 return;
44822             }
44823             this.cblack.push(tag);
44824             
44825         }, this);
44826         
44827         Roo.each(b, function(tag) {
44828             if (w.indexOf(tag) > -1) {
44829                 return;
44830             }
44831             if (this.cblack.indexOf(tag) > -1) {
44832                 return;
44833             }
44834             this.cblack.push(tag);
44835             
44836         }, this);
44837     },
44838     
44839     setStylesheets : function(stylesheets)
44840     {
44841         if(typeof(stylesheets) == 'string'){
44842             Roo.get(this.iframe.contentDocument.head).createChild({
44843                 tag : 'link',
44844                 rel : 'stylesheet',
44845                 type : 'text/css',
44846                 href : stylesheets
44847             });
44848             
44849             return;
44850         }
44851         var _this = this;
44852      
44853         Roo.each(stylesheets, function(s) {
44854             if(!s.length){
44855                 return;
44856             }
44857             
44858             Roo.get(_this.iframe.contentDocument.head).createChild({
44859                 tag : 'link',
44860                 rel : 'stylesheet',
44861                 type : 'text/css',
44862                 href : s
44863             });
44864         });
44865
44866         
44867     },
44868     
44869     removeStylesheets : function()
44870     {
44871         var _this = this;
44872         
44873         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
44874             s.remove();
44875         });
44876     },
44877     
44878     setStyle : function(style)
44879     {
44880         Roo.get(this.iframe.contentDocument.head).createChild({
44881             tag : 'style',
44882             type : 'text/css',
44883             html : style
44884         });
44885
44886         return;
44887     }
44888     
44889     // hide stuff that is not compatible
44890     /**
44891      * @event blur
44892      * @hide
44893      */
44894     /**
44895      * @event change
44896      * @hide
44897      */
44898     /**
44899      * @event focus
44900      * @hide
44901      */
44902     /**
44903      * @event specialkey
44904      * @hide
44905      */
44906     /**
44907      * @cfg {String} fieldClass @hide
44908      */
44909     /**
44910      * @cfg {String} focusClass @hide
44911      */
44912     /**
44913      * @cfg {String} autoCreate @hide
44914      */
44915     /**
44916      * @cfg {String} inputType @hide
44917      */
44918     /**
44919      * @cfg {String} invalidClass @hide
44920      */
44921     /**
44922      * @cfg {String} invalidText @hide
44923      */
44924     /**
44925      * @cfg {String} msgFx @hide
44926      */
44927     /**
44928      * @cfg {String} validateOnBlur @hide
44929      */
44930 });
44931
44932 Roo.HtmlEditorCore.white = [
44933         'area', 'br', 'img', 'input', 'hr', 'wbr',
44934         
44935        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
44936        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
44937        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
44938        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
44939        'table',   'ul',         'xmp', 
44940        
44941        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
44942       'thead',   'tr', 
44943      
44944       'dir', 'menu', 'ol', 'ul', 'dl',
44945        
44946       'embed',  'object'
44947 ];
44948
44949
44950 Roo.HtmlEditorCore.black = [
44951     //    'embed',  'object', // enable - backend responsiblity to clean thiese
44952         'applet', // 
44953         'base',   'basefont', 'bgsound', 'blink',  'body', 
44954         'frame',  'frameset', 'head',    'html',   'ilayer', 
44955         'iframe', 'layer',  'link',     'meta',    'object',   
44956         'script', 'style' ,'title',  'xml' // clean later..
44957 ];
44958 Roo.HtmlEditorCore.clean = [
44959     'script', 'style', 'title', 'xml'
44960 ];
44961 Roo.HtmlEditorCore.remove = [
44962     'font'
44963 ];
44964 // attributes..
44965
44966 Roo.HtmlEditorCore.ablack = [
44967     'on'
44968 ];
44969     
44970 Roo.HtmlEditorCore.aclean = [ 
44971     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
44972 ];
44973
44974 // protocols..
44975 Roo.HtmlEditorCore.pwhite= [
44976         'http',  'https',  'mailto'
44977 ];
44978
44979 // white listed style attributes.
44980 Roo.HtmlEditorCore.cwhite= [
44981       //  'text-align', /// default is to allow most things..
44982       
44983          
44984 //        'font-size'//??
44985 ];
44986
44987 // black listed style attributes.
44988 Roo.HtmlEditorCore.cblack= [
44989       //  'font-size' -- this can be set by the project 
44990 ];
44991
44992
44993 Roo.HtmlEditorCore.swapCodes   =[ 
44994     [    8211, "--" ], 
44995     [    8212, "--" ], 
44996     [    8216,  "'" ],  
44997     [    8217, "'" ],  
44998     [    8220, '"' ],  
44999     [    8221, '"' ],  
45000     [    8226, "*" ],  
45001     [    8230, "..." ]
45002 ]; 
45003
45004     //<script type="text/javascript">
45005
45006 /*
45007  * Ext JS Library 1.1.1
45008  * Copyright(c) 2006-2007, Ext JS, LLC.
45009  * Licence LGPL
45010  * 
45011  */
45012  
45013  
45014 Roo.form.HtmlEditor = function(config){
45015     
45016     
45017     
45018     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45019     
45020     if (!this.toolbars) {
45021         this.toolbars = [];
45022     }
45023     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45024     
45025     
45026 };
45027
45028 /**
45029  * @class Roo.form.HtmlEditor
45030  * @extends Roo.form.Field
45031  * Provides a lightweight HTML Editor component.
45032  *
45033  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45034  * 
45035  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45036  * supported by this editor.</b><br/><br/>
45037  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45038  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45039  */
45040 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45041     /**
45042      * @cfg {Boolean} clearUp
45043      */
45044     clearUp : true,
45045       /**
45046      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45047      */
45048     toolbars : false,
45049    
45050      /**
45051      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45052      *                        Roo.resizable.
45053      */
45054     resizable : false,
45055      /**
45056      * @cfg {Number} height (in pixels)
45057      */   
45058     height: 300,
45059    /**
45060      * @cfg {Number} width (in pixels)
45061      */   
45062     width: 500,
45063     
45064     /**
45065      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45066      * 
45067      */
45068     stylesheets: false,
45069     
45070     
45071      /**
45072      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45073      * 
45074      */
45075     cblack: false,
45076     /**
45077      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45078      * 
45079      */
45080     cwhite: false,
45081     
45082      /**
45083      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45084      * 
45085      */
45086     black: false,
45087     /**
45088      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45089      * 
45090      */
45091     white: false,
45092     
45093     // id of frame..
45094     frameId: false,
45095     
45096     // private properties
45097     validationEvent : false,
45098     deferHeight: true,
45099     initialized : false,
45100     activated : false,
45101     
45102     onFocus : Roo.emptyFn,
45103     iframePad:3,
45104     hideMode:'offsets',
45105     
45106     actionMode : 'container', // defaults to hiding it...
45107     
45108     defaultAutoCreate : { // modified by initCompnoent..
45109         tag: "textarea",
45110         style:"width:500px;height:300px;",
45111         autocomplete: "new-password"
45112     },
45113
45114     // private
45115     initComponent : function(){
45116         this.addEvents({
45117             /**
45118              * @event initialize
45119              * Fires when the editor is fully initialized (including the iframe)
45120              * @param {HtmlEditor} this
45121              */
45122             initialize: true,
45123             /**
45124              * @event activate
45125              * Fires when the editor is first receives the focus. Any insertion must wait
45126              * until after this event.
45127              * @param {HtmlEditor} this
45128              */
45129             activate: true,
45130              /**
45131              * @event beforesync
45132              * Fires before the textarea is updated with content from the editor iframe. Return false
45133              * to cancel the sync.
45134              * @param {HtmlEditor} this
45135              * @param {String} html
45136              */
45137             beforesync: true,
45138              /**
45139              * @event beforepush
45140              * Fires before the iframe editor is updated with content from the textarea. Return false
45141              * to cancel the push.
45142              * @param {HtmlEditor} this
45143              * @param {String} html
45144              */
45145             beforepush: true,
45146              /**
45147              * @event sync
45148              * Fires when the textarea is updated with content from the editor iframe.
45149              * @param {HtmlEditor} this
45150              * @param {String} html
45151              */
45152             sync: true,
45153              /**
45154              * @event push
45155              * Fires when the iframe editor is updated with content from the textarea.
45156              * @param {HtmlEditor} this
45157              * @param {String} html
45158              */
45159             push: true,
45160              /**
45161              * @event editmodechange
45162              * Fires when the editor switches edit modes
45163              * @param {HtmlEditor} this
45164              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45165              */
45166             editmodechange: true,
45167             /**
45168              * @event editorevent
45169              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45170              * @param {HtmlEditor} this
45171              */
45172             editorevent: true,
45173             /**
45174              * @event firstfocus
45175              * Fires when on first focus - needed by toolbars..
45176              * @param {HtmlEditor} this
45177              */
45178             firstfocus: true,
45179             /**
45180              * @event autosave
45181              * Auto save the htmlEditor value as a file into Events
45182              * @param {HtmlEditor} this
45183              */
45184             autosave: true,
45185             /**
45186              * @event savedpreview
45187              * preview the saved version of htmlEditor
45188              * @param {HtmlEditor} this
45189              */
45190             savedpreview: true,
45191             
45192             /**
45193             * @event stylesheetsclick
45194             * Fires when press the Sytlesheets button
45195             * @param {Roo.HtmlEditorCore} this
45196             */
45197             stylesheetsclick: true
45198         });
45199         this.defaultAutoCreate =  {
45200             tag: "textarea",
45201             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45202             autocomplete: "new-password"
45203         };
45204     },
45205
45206     /**
45207      * Protected method that will not generally be called directly. It
45208      * is called when the editor creates its toolbar. Override this method if you need to
45209      * add custom toolbar buttons.
45210      * @param {HtmlEditor} editor
45211      */
45212     createToolbar : function(editor){
45213         Roo.log("create toolbars");
45214         if (!editor.toolbars || !editor.toolbars.length) {
45215             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45216         }
45217         
45218         for (var i =0 ; i < editor.toolbars.length;i++) {
45219             editor.toolbars[i] = Roo.factory(
45220                     typeof(editor.toolbars[i]) == 'string' ?
45221                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45222                 Roo.form.HtmlEditor);
45223             editor.toolbars[i].init(editor);
45224         }
45225          
45226         
45227     },
45228
45229      
45230     // private
45231     onRender : function(ct, position)
45232     {
45233         var _t = this;
45234         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45235         
45236         this.wrap = this.el.wrap({
45237             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45238         });
45239         
45240         this.editorcore.onRender(ct, position);
45241          
45242         if (this.resizable) {
45243             this.resizeEl = new Roo.Resizable(this.wrap, {
45244                 pinned : true,
45245                 wrap: true,
45246                 dynamic : true,
45247                 minHeight : this.height,
45248                 height: this.height,
45249                 handles : this.resizable,
45250                 width: this.width,
45251                 listeners : {
45252                     resize : function(r, w, h) {
45253                         _t.onResize(w,h); // -something
45254                     }
45255                 }
45256             });
45257             
45258         }
45259         this.createToolbar(this);
45260        
45261         
45262         if(!this.width){
45263             this.setSize(this.wrap.getSize());
45264         }
45265         if (this.resizeEl) {
45266             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45267             // should trigger onReize..
45268         }
45269         
45270         this.keyNav = new Roo.KeyNav(this.el, {
45271             
45272             "tab" : function(e){
45273                 e.preventDefault();
45274                 
45275                 var value = this.getValue();
45276                 
45277                 var start = this.el.dom.selectionStart;
45278                 var end = this.el.dom.selectionEnd;
45279                 
45280                 if(!e.shiftKey){
45281                     
45282                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45283                     this.el.dom.setSelectionRange(end + 1, end + 1);
45284                     return;
45285                 }
45286                 
45287                 var f = value.substring(0, start).split("\t");
45288                 
45289                 if(f.pop().length != 0){
45290                     return;
45291                 }
45292                 
45293                 this.setValue(f.join("\t") + value.substring(end));
45294                 this.el.dom.setSelectionRange(start - 1, start - 1);
45295                 
45296             },
45297             
45298             "home" : function(e){
45299                 e.preventDefault();
45300                 
45301                 var curr = this.el.dom.selectionStart;
45302                 var lines = this.getValue().split("\n");
45303                 
45304                 if(!lines.length){
45305                     return;
45306                 }
45307                 
45308                 if(e.ctrlKey){
45309                     this.el.dom.setSelectionRange(0, 0);
45310                     return;
45311                 }
45312                 
45313                 var pos = 0;
45314                 
45315                 for (var i = 0; i < lines.length;i++) {
45316                     pos += lines[i].length;
45317                     
45318                     if(i != 0){
45319                         pos += 1;
45320                     }
45321                     
45322                     if(pos < curr){
45323                         continue;
45324                     }
45325                     
45326                     pos -= lines[i].length;
45327                     
45328                     break;
45329                 }
45330                 
45331                 if(!e.shiftKey){
45332                     this.el.dom.setSelectionRange(pos, pos);
45333                     return;
45334                 }
45335                 
45336                 this.el.dom.selectionStart = pos;
45337                 this.el.dom.selectionEnd = curr;
45338             },
45339             
45340             "end" : function(e){
45341                 e.preventDefault();
45342                 
45343                 var curr = this.el.dom.selectionStart;
45344                 var lines = this.getValue().split("\n");
45345                 
45346                 if(!lines.length){
45347                     return;
45348                 }
45349                 
45350                 if(e.ctrlKey){
45351                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45352                     return;
45353                 }
45354                 
45355                 var pos = 0;
45356                 
45357                 for (var i = 0; i < lines.length;i++) {
45358                     
45359                     pos += lines[i].length;
45360                     
45361                     if(i != 0){
45362                         pos += 1;
45363                     }
45364                     
45365                     if(pos < curr){
45366                         continue;
45367                     }
45368                     
45369                     break;
45370                 }
45371                 
45372                 if(!e.shiftKey){
45373                     this.el.dom.setSelectionRange(pos, pos);
45374                     return;
45375                 }
45376                 
45377                 this.el.dom.selectionStart = curr;
45378                 this.el.dom.selectionEnd = pos;
45379             },
45380
45381             scope : this,
45382
45383             doRelay : function(foo, bar, hname){
45384                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45385             },
45386
45387             forceKeyDown: true
45388         });
45389         
45390 //        if(this.autosave && this.w){
45391 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45392 //        }
45393     },
45394
45395     // private
45396     onResize : function(w, h)
45397     {
45398         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45399         var ew = false;
45400         var eh = false;
45401         
45402         if(this.el ){
45403             if(typeof w == 'number'){
45404                 var aw = w - this.wrap.getFrameWidth('lr');
45405                 this.el.setWidth(this.adjustWidth('textarea', aw));
45406                 ew = aw;
45407             }
45408             if(typeof h == 'number'){
45409                 var tbh = 0;
45410                 for (var i =0; i < this.toolbars.length;i++) {
45411                     // fixme - ask toolbars for heights?
45412                     tbh += this.toolbars[i].tb.el.getHeight();
45413                     if (this.toolbars[i].footer) {
45414                         tbh += this.toolbars[i].footer.el.getHeight();
45415                     }
45416                 }
45417                 
45418                 
45419                 
45420                 
45421                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45422                 ah -= 5; // knock a few pixes off for look..
45423 //                Roo.log(ah);
45424                 this.el.setHeight(this.adjustWidth('textarea', ah));
45425                 var eh = ah;
45426             }
45427         }
45428         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45429         this.editorcore.onResize(ew,eh);
45430         
45431     },
45432
45433     /**
45434      * Toggles the editor between standard and source edit mode.
45435      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45436      */
45437     toggleSourceEdit : function(sourceEditMode)
45438     {
45439         this.editorcore.toggleSourceEdit(sourceEditMode);
45440         
45441         if(this.editorcore.sourceEditMode){
45442             Roo.log('editor - showing textarea');
45443             
45444 //            Roo.log('in');
45445 //            Roo.log(this.syncValue());
45446             this.editorcore.syncValue();
45447             this.el.removeClass('x-hidden');
45448             this.el.dom.removeAttribute('tabIndex');
45449             this.el.focus();
45450             
45451             for (var i = 0; i < this.toolbars.length; i++) {
45452                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45453                     this.toolbars[i].tb.hide();
45454                     this.toolbars[i].footer.hide();
45455                 }
45456             }
45457             
45458         }else{
45459             Roo.log('editor - hiding textarea');
45460 //            Roo.log('out')
45461 //            Roo.log(this.pushValue()); 
45462             this.editorcore.pushValue();
45463             
45464             this.el.addClass('x-hidden');
45465             this.el.dom.setAttribute('tabIndex', -1);
45466             
45467             for (var i = 0; i < this.toolbars.length; i++) {
45468                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45469                     this.toolbars[i].tb.show();
45470                     this.toolbars[i].footer.show();
45471                 }
45472             }
45473             
45474             //this.deferFocus();
45475         }
45476         
45477         this.setSize(this.wrap.getSize());
45478         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45479         
45480         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45481     },
45482  
45483     // private (for BoxComponent)
45484     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45485
45486     // private (for BoxComponent)
45487     getResizeEl : function(){
45488         return this.wrap;
45489     },
45490
45491     // private (for BoxComponent)
45492     getPositionEl : function(){
45493         return this.wrap;
45494     },
45495
45496     // private
45497     initEvents : function(){
45498         this.originalValue = this.getValue();
45499     },
45500
45501     /**
45502      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45503      * @method
45504      */
45505     markInvalid : Roo.emptyFn,
45506     /**
45507      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45508      * @method
45509      */
45510     clearInvalid : Roo.emptyFn,
45511
45512     setValue : function(v){
45513         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45514         this.editorcore.pushValue();
45515     },
45516
45517      
45518     // private
45519     deferFocus : function(){
45520         this.focus.defer(10, this);
45521     },
45522
45523     // doc'ed in Field
45524     focus : function(){
45525         this.editorcore.focus();
45526         
45527     },
45528       
45529
45530     // private
45531     onDestroy : function(){
45532         
45533         
45534         
45535         if(this.rendered){
45536             
45537             for (var i =0; i < this.toolbars.length;i++) {
45538                 // fixme - ask toolbars for heights?
45539                 this.toolbars[i].onDestroy();
45540             }
45541             
45542             this.wrap.dom.innerHTML = '';
45543             this.wrap.remove();
45544         }
45545     },
45546
45547     // private
45548     onFirstFocus : function(){
45549         //Roo.log("onFirstFocus");
45550         this.editorcore.onFirstFocus();
45551          for (var i =0; i < this.toolbars.length;i++) {
45552             this.toolbars[i].onFirstFocus();
45553         }
45554         
45555     },
45556     
45557     // private
45558     syncValue : function()
45559     {
45560         this.editorcore.syncValue();
45561     },
45562     
45563     pushValue : function()
45564     {
45565         this.editorcore.pushValue();
45566     },
45567     
45568     setStylesheets : function(stylesheets)
45569     {
45570         this.editorcore.setStylesheets(stylesheets);
45571     },
45572     
45573     removeStylesheets : function()
45574     {
45575         this.editorcore.removeStylesheets();
45576     }
45577      
45578     
45579     // hide stuff that is not compatible
45580     /**
45581      * @event blur
45582      * @hide
45583      */
45584     /**
45585      * @event change
45586      * @hide
45587      */
45588     /**
45589      * @event focus
45590      * @hide
45591      */
45592     /**
45593      * @event specialkey
45594      * @hide
45595      */
45596     /**
45597      * @cfg {String} fieldClass @hide
45598      */
45599     /**
45600      * @cfg {String} focusClass @hide
45601      */
45602     /**
45603      * @cfg {String} autoCreate @hide
45604      */
45605     /**
45606      * @cfg {String} inputType @hide
45607      */
45608     /**
45609      * @cfg {String} invalidClass @hide
45610      */
45611     /**
45612      * @cfg {String} invalidText @hide
45613      */
45614     /**
45615      * @cfg {String} msgFx @hide
45616      */
45617     /**
45618      * @cfg {String} validateOnBlur @hide
45619      */
45620 });
45621  
45622     // <script type="text/javascript">
45623 /*
45624  * Based on
45625  * Ext JS Library 1.1.1
45626  * Copyright(c) 2006-2007, Ext JS, LLC.
45627  *  
45628  
45629  */
45630
45631 /**
45632  * @class Roo.form.HtmlEditorToolbar1
45633  * Basic Toolbar
45634  * 
45635  * Usage:
45636  *
45637  new Roo.form.HtmlEditor({
45638     ....
45639     toolbars : [
45640         new Roo.form.HtmlEditorToolbar1({
45641             disable : { fonts: 1 , format: 1, ..., ... , ...],
45642             btns : [ .... ]
45643         })
45644     }
45645      
45646  * 
45647  * @cfg {Object} disable List of elements to disable..
45648  * @cfg {Array} btns List of additional buttons.
45649  * 
45650  * 
45651  * NEEDS Extra CSS? 
45652  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45653  */
45654  
45655 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45656 {
45657     
45658     Roo.apply(this, config);
45659     
45660     // default disabled, based on 'good practice'..
45661     this.disable = this.disable || {};
45662     Roo.applyIf(this.disable, {
45663         fontSize : true,
45664         colors : true,
45665         specialElements : true
45666     });
45667     
45668     
45669     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
45670     // dont call parent... till later.
45671 }
45672
45673 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
45674     
45675     tb: false,
45676     
45677     rendered: false,
45678     
45679     editor : false,
45680     editorcore : false,
45681     /**
45682      * @cfg {Object} disable  List of toolbar elements to disable
45683          
45684      */
45685     disable : false,
45686     
45687     
45688      /**
45689      * @cfg {String} createLinkText The default text for the create link prompt
45690      */
45691     createLinkText : 'Please enter the URL for the link:',
45692     /**
45693      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
45694      */
45695     defaultLinkValue : 'http:/'+'/',
45696    
45697     
45698       /**
45699      * @cfg {Array} fontFamilies An array of available font families
45700      */
45701     fontFamilies : [
45702         'Arial',
45703         'Courier New',
45704         'Tahoma',
45705         'Times New Roman',
45706         'Verdana'
45707     ],
45708     
45709     specialChars : [
45710            "&#169;",
45711           "&#174;",     
45712           "&#8482;",    
45713           "&#163;" ,    
45714          // "&#8212;",    
45715           "&#8230;",    
45716           "&#247;" ,    
45717         //  "&#225;" ,     ?? a acute?
45718            "&#8364;"    , //Euro
45719        //   "&#8220;"    ,
45720         //  "&#8221;"    ,
45721         //  "&#8226;"    ,
45722           "&#176;"  //   , // degrees
45723
45724          // "&#233;"     , // e ecute
45725          // "&#250;"     , // u ecute?
45726     ],
45727     
45728     specialElements : [
45729         {
45730             text: "Insert Table",
45731             xtype: 'MenuItem',
45732             xns : Roo.Menu,
45733             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
45734                 
45735         },
45736         {    
45737             text: "Insert Image",
45738             xtype: 'MenuItem',
45739             xns : Roo.Menu,
45740             ihtml : '<img src="about:blank"/>'
45741             
45742         }
45743         
45744          
45745     ],
45746     
45747     
45748     inputElements : [ 
45749             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
45750             "input:submit", "input:button", "select", "textarea", "label" ],
45751     formats : [
45752         ["p"] ,  
45753         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
45754         ["pre"],[ "code"], 
45755         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
45756         ['div'],['span'],
45757         ['sup'],['sub']
45758     ],
45759     
45760     cleanStyles : [
45761         "font-size"
45762     ],
45763      /**
45764      * @cfg {String} defaultFont default font to use.
45765      */
45766     defaultFont: 'tahoma',
45767    
45768     fontSelect : false,
45769     
45770     
45771     formatCombo : false,
45772     
45773     init : function(editor)
45774     {
45775         this.editor = editor;
45776         this.editorcore = editor.editorcore ? editor.editorcore : editor;
45777         var editorcore = this.editorcore;
45778         
45779         var _t = this;
45780         
45781         var fid = editorcore.frameId;
45782         var etb = this;
45783         function btn(id, toggle, handler){
45784             var xid = fid + '-'+ id ;
45785             return {
45786                 id : xid,
45787                 cmd : id,
45788                 cls : 'x-btn-icon x-edit-'+id,
45789                 enableToggle:toggle !== false,
45790                 scope: _t, // was editor...
45791                 handler:handler||_t.relayBtnCmd,
45792                 clickEvent:'mousedown',
45793                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
45794                 tabIndex:-1
45795             };
45796         }
45797         
45798         
45799         
45800         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
45801         this.tb = tb;
45802          // stop form submits
45803         tb.el.on('click', function(e){
45804             e.preventDefault(); // what does this do?
45805         });
45806
45807         if(!this.disable.font) { // && !Roo.isSafari){
45808             /* why no safari for fonts 
45809             editor.fontSelect = tb.el.createChild({
45810                 tag:'select',
45811                 tabIndex: -1,
45812                 cls:'x-font-select',
45813                 html: this.createFontOptions()
45814             });
45815             
45816             editor.fontSelect.on('change', function(){
45817                 var font = editor.fontSelect.dom.value;
45818                 editor.relayCmd('fontname', font);
45819                 editor.deferFocus();
45820             }, editor);
45821             
45822             tb.add(
45823                 editor.fontSelect.dom,
45824                 '-'
45825             );
45826             */
45827             
45828         };
45829         if(!this.disable.formats){
45830             this.formatCombo = new Roo.form.ComboBox({
45831                 store: new Roo.data.SimpleStore({
45832                     id : 'tag',
45833                     fields: ['tag'],
45834                     data : this.formats // from states.js
45835                 }),
45836                 blockFocus : true,
45837                 name : '',
45838                 //autoCreate : {tag: "div",  size: "20"},
45839                 displayField:'tag',
45840                 typeAhead: false,
45841                 mode: 'local',
45842                 editable : false,
45843                 triggerAction: 'all',
45844                 emptyText:'Add tag',
45845                 selectOnFocus:true,
45846                 width:135,
45847                 listeners : {
45848                     'select': function(c, r, i) {
45849                         editorcore.insertTag(r.get('tag'));
45850                         editor.focus();
45851                     }
45852                 }
45853
45854             });
45855             tb.addField(this.formatCombo);
45856             
45857         }
45858         
45859         if(!this.disable.format){
45860             tb.add(
45861                 btn('bold'),
45862                 btn('italic'),
45863                 btn('underline'),
45864                 btn('strikethrough')
45865             );
45866         };
45867         if(!this.disable.fontSize){
45868             tb.add(
45869                 '-',
45870                 
45871                 
45872                 btn('increasefontsize', false, editorcore.adjustFont),
45873                 btn('decreasefontsize', false, editorcore.adjustFont)
45874             );
45875         };
45876         
45877         
45878         if(!this.disable.colors){
45879             tb.add(
45880                 '-', {
45881                     id:editorcore.frameId +'-forecolor',
45882                     cls:'x-btn-icon x-edit-forecolor',
45883                     clickEvent:'mousedown',
45884                     tooltip: this.buttonTips['forecolor'] || undefined,
45885                     tabIndex:-1,
45886                     menu : new Roo.menu.ColorMenu({
45887                         allowReselect: true,
45888                         focus: Roo.emptyFn,
45889                         value:'000000',
45890                         plain:true,
45891                         selectHandler: function(cp, color){
45892                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
45893                             editor.deferFocus();
45894                         },
45895                         scope: editorcore,
45896                         clickEvent:'mousedown'
45897                     })
45898                 }, {
45899                     id:editorcore.frameId +'backcolor',
45900                     cls:'x-btn-icon x-edit-backcolor',
45901                     clickEvent:'mousedown',
45902                     tooltip: this.buttonTips['backcolor'] || undefined,
45903                     tabIndex:-1,
45904                     menu : new Roo.menu.ColorMenu({
45905                         focus: Roo.emptyFn,
45906                         value:'FFFFFF',
45907                         plain:true,
45908                         allowReselect: true,
45909                         selectHandler: function(cp, color){
45910                             if(Roo.isGecko){
45911                                 editorcore.execCmd('useCSS', false);
45912                                 editorcore.execCmd('hilitecolor', color);
45913                                 editorcore.execCmd('useCSS', true);
45914                                 editor.deferFocus();
45915                             }else{
45916                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
45917                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
45918                                 editor.deferFocus();
45919                             }
45920                         },
45921                         scope:editorcore,
45922                         clickEvent:'mousedown'
45923                     })
45924                 }
45925             );
45926         };
45927         // now add all the items...
45928         
45929
45930         if(!this.disable.alignments){
45931             tb.add(
45932                 '-',
45933                 btn('justifyleft'),
45934                 btn('justifycenter'),
45935                 btn('justifyright')
45936             );
45937         };
45938
45939         //if(!Roo.isSafari){
45940             if(!this.disable.links){
45941                 tb.add(
45942                     '-',
45943                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
45944                 );
45945             };
45946
45947             if(!this.disable.lists){
45948                 tb.add(
45949                     '-',
45950                     btn('insertorderedlist'),
45951                     btn('insertunorderedlist')
45952                 );
45953             }
45954             if(!this.disable.sourceEdit){
45955                 tb.add(
45956                     '-',
45957                     btn('sourceedit', true, function(btn){
45958                         this.toggleSourceEdit(btn.pressed);
45959                     })
45960                 );
45961             }
45962         //}
45963         
45964         var smenu = { };
45965         // special menu.. - needs to be tidied up..
45966         if (!this.disable.special) {
45967             smenu = {
45968                 text: "&#169;",
45969                 cls: 'x-edit-none',
45970                 
45971                 menu : {
45972                     items : []
45973                 }
45974             };
45975             for (var i =0; i < this.specialChars.length; i++) {
45976                 smenu.menu.items.push({
45977                     
45978                     html: this.specialChars[i],
45979                     handler: function(a,b) {
45980                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
45981                         //editor.insertAtCursor(a.html);
45982                         
45983                     },
45984                     tabIndex:-1
45985                 });
45986             }
45987             
45988             
45989             tb.add(smenu);
45990             
45991             
45992         }
45993         
45994         var cmenu = { };
45995         if (!this.disable.cleanStyles) {
45996             cmenu = {
45997                 cls: 'x-btn-icon x-btn-clear',
45998                 
45999                 menu : {
46000                     items : []
46001                 }
46002             };
46003             for (var i =0; i < this.cleanStyles.length; i++) {
46004                 cmenu.menu.items.push({
46005                     actiontype : this.cleanStyles[i],
46006                     html: 'Remove ' + this.cleanStyles[i],
46007                     handler: function(a,b) {
46008 //                        Roo.log(a);
46009 //                        Roo.log(b);
46010                         var c = Roo.get(editorcore.doc.body);
46011                         c.select('[style]').each(function(s) {
46012                             s.dom.style.removeProperty(a.actiontype);
46013                         });
46014                         editorcore.syncValue();
46015                     },
46016                     tabIndex:-1
46017                 });
46018             }
46019              cmenu.menu.items.push({
46020                 actiontype : 'tablewidths',
46021                 html: 'Remove Table Widths',
46022                 handler: function(a,b) {
46023                     editorcore.cleanTableWidths();
46024                     editorcore.syncValue();
46025                 },
46026                 tabIndex:-1
46027             });
46028             cmenu.menu.items.push({
46029                 actiontype : 'word',
46030                 html: 'Remove MS Word Formating',
46031                 handler: function(a,b) {
46032                     editorcore.cleanWord();
46033                     editorcore.syncValue();
46034                 },
46035                 tabIndex:-1
46036             });
46037             
46038             cmenu.menu.items.push({
46039                 actiontype : 'all',
46040                 html: 'Remove All Styles',
46041                 handler: function(a,b) {
46042                     
46043                     var c = Roo.get(editorcore.doc.body);
46044                     c.select('[style]').each(function(s) {
46045                         s.dom.removeAttribute('style');
46046                     });
46047                     editorcore.syncValue();
46048                 },
46049                 tabIndex:-1
46050             });
46051             
46052             cmenu.menu.items.push({
46053                 actiontype : 'all',
46054                 html: 'Remove All CSS Classes',
46055                 handler: function(a,b) {
46056                     
46057                     var c = Roo.get(editorcore.doc.body);
46058                     c.select('[class]').each(function(s) {
46059                         s.dom.removeAttribute('class');
46060                     });
46061                     editorcore.cleanWord();
46062                     editorcore.syncValue();
46063                 },
46064                 tabIndex:-1
46065             });
46066             
46067              cmenu.menu.items.push({
46068                 actiontype : 'tidy',
46069                 html: 'Tidy HTML Source',
46070                 handler: function(a,b) {
46071                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46072                     editorcore.syncValue();
46073                 },
46074                 tabIndex:-1
46075             });
46076             
46077             
46078             tb.add(cmenu);
46079         }
46080          
46081         if (!this.disable.specialElements) {
46082             var semenu = {
46083                 text: "Other;",
46084                 cls: 'x-edit-none',
46085                 menu : {
46086                     items : []
46087                 }
46088             };
46089             for (var i =0; i < this.specialElements.length; i++) {
46090                 semenu.menu.items.push(
46091                     Roo.apply({ 
46092                         handler: function(a,b) {
46093                             editor.insertAtCursor(this.ihtml);
46094                         }
46095                     }, this.specialElements[i])
46096                 );
46097                     
46098             }
46099             
46100             tb.add(semenu);
46101             
46102             
46103         }
46104          
46105         
46106         if (this.btns) {
46107             for(var i =0; i< this.btns.length;i++) {
46108                 var b = Roo.factory(this.btns[i],Roo.form);
46109                 b.cls =  'x-edit-none';
46110                 
46111                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46112                     b.cls += ' x-init-enable';
46113                 }
46114                 
46115                 b.scope = editorcore;
46116                 tb.add(b);
46117             }
46118         
46119         }
46120         
46121         
46122         
46123         // disable everything...
46124         
46125         this.tb.items.each(function(item){
46126             
46127            if(
46128                 item.id != editorcore.frameId+ '-sourceedit' && 
46129                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46130             ){
46131                 
46132                 item.disable();
46133             }
46134         });
46135         this.rendered = true;
46136         
46137         // the all the btns;
46138         editor.on('editorevent', this.updateToolbar, this);
46139         // other toolbars need to implement this..
46140         //editor.on('editmodechange', this.updateToolbar, this);
46141     },
46142     
46143     
46144     relayBtnCmd : function(btn) {
46145         this.editorcore.relayCmd(btn.cmd);
46146     },
46147     // private used internally
46148     createLink : function(){
46149         Roo.log("create link?");
46150         var url = prompt(this.createLinkText, this.defaultLinkValue);
46151         if(url && url != 'http:/'+'/'){
46152             this.editorcore.relayCmd('createlink', url);
46153         }
46154     },
46155
46156     
46157     /**
46158      * Protected method that will not generally be called directly. It triggers
46159      * a toolbar update by reading the markup state of the current selection in the editor.
46160      */
46161     updateToolbar: function(){
46162
46163         if(!this.editorcore.activated){
46164             this.editor.onFirstFocus();
46165             return;
46166         }
46167
46168         var btns = this.tb.items.map, 
46169             doc = this.editorcore.doc,
46170             frameId = this.editorcore.frameId;
46171
46172         if(!this.disable.font && !Roo.isSafari){
46173             /*
46174             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46175             if(name != this.fontSelect.dom.value){
46176                 this.fontSelect.dom.value = name;
46177             }
46178             */
46179         }
46180         if(!this.disable.format){
46181             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46182             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46183             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46184             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46185         }
46186         if(!this.disable.alignments){
46187             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46188             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46189             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46190         }
46191         if(!Roo.isSafari && !this.disable.lists){
46192             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46193             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46194         }
46195         
46196         var ans = this.editorcore.getAllAncestors();
46197         if (this.formatCombo) {
46198             
46199             
46200             var store = this.formatCombo.store;
46201             this.formatCombo.setValue("");
46202             for (var i =0; i < ans.length;i++) {
46203                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46204                     // select it..
46205                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46206                     break;
46207                 }
46208             }
46209         }
46210         
46211         
46212         
46213         // hides menus... - so this cant be on a menu...
46214         Roo.menu.MenuMgr.hideAll();
46215
46216         //this.editorsyncValue();
46217     },
46218    
46219     
46220     createFontOptions : function(){
46221         var buf = [], fs = this.fontFamilies, ff, lc;
46222         
46223         
46224         
46225         for(var i = 0, len = fs.length; i< len; i++){
46226             ff = fs[i];
46227             lc = ff.toLowerCase();
46228             buf.push(
46229                 '<option value="',lc,'" style="font-family:',ff,';"',
46230                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46231                     ff,
46232                 '</option>'
46233             );
46234         }
46235         return buf.join('');
46236     },
46237     
46238     toggleSourceEdit : function(sourceEditMode){
46239         
46240         Roo.log("toolbar toogle");
46241         if(sourceEditMode === undefined){
46242             sourceEditMode = !this.sourceEditMode;
46243         }
46244         this.sourceEditMode = sourceEditMode === true;
46245         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46246         // just toggle the button?
46247         if(btn.pressed !== this.sourceEditMode){
46248             btn.toggle(this.sourceEditMode);
46249             return;
46250         }
46251         
46252         if(sourceEditMode){
46253             Roo.log("disabling buttons");
46254             this.tb.items.each(function(item){
46255                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46256                     item.disable();
46257                 }
46258             });
46259           
46260         }else{
46261             Roo.log("enabling buttons");
46262             if(this.editorcore.initialized){
46263                 this.tb.items.each(function(item){
46264                     item.enable();
46265                 });
46266             }
46267             
46268         }
46269         Roo.log("calling toggole on editor");
46270         // tell the editor that it's been pressed..
46271         this.editor.toggleSourceEdit(sourceEditMode);
46272        
46273     },
46274      /**
46275      * Object collection of toolbar tooltips for the buttons in the editor. The key
46276      * is the command id associated with that button and the value is a valid QuickTips object.
46277      * For example:
46278 <pre><code>
46279 {
46280     bold : {
46281         title: 'Bold (Ctrl+B)',
46282         text: 'Make the selected text bold.',
46283         cls: 'x-html-editor-tip'
46284     },
46285     italic : {
46286         title: 'Italic (Ctrl+I)',
46287         text: 'Make the selected text italic.',
46288         cls: 'x-html-editor-tip'
46289     },
46290     ...
46291 </code></pre>
46292     * @type Object
46293      */
46294     buttonTips : {
46295         bold : {
46296             title: 'Bold (Ctrl+B)',
46297             text: 'Make the selected text bold.',
46298             cls: 'x-html-editor-tip'
46299         },
46300         italic : {
46301             title: 'Italic (Ctrl+I)',
46302             text: 'Make the selected text italic.',
46303             cls: 'x-html-editor-tip'
46304         },
46305         underline : {
46306             title: 'Underline (Ctrl+U)',
46307             text: 'Underline the selected text.',
46308             cls: 'x-html-editor-tip'
46309         },
46310         strikethrough : {
46311             title: 'Strikethrough',
46312             text: 'Strikethrough the selected text.',
46313             cls: 'x-html-editor-tip'
46314         },
46315         increasefontsize : {
46316             title: 'Grow Text',
46317             text: 'Increase the font size.',
46318             cls: 'x-html-editor-tip'
46319         },
46320         decreasefontsize : {
46321             title: 'Shrink Text',
46322             text: 'Decrease the font size.',
46323             cls: 'x-html-editor-tip'
46324         },
46325         backcolor : {
46326             title: 'Text Highlight Color',
46327             text: 'Change the background color of the selected text.',
46328             cls: 'x-html-editor-tip'
46329         },
46330         forecolor : {
46331             title: 'Font Color',
46332             text: 'Change the color of the selected text.',
46333             cls: 'x-html-editor-tip'
46334         },
46335         justifyleft : {
46336             title: 'Align Text Left',
46337             text: 'Align text to the left.',
46338             cls: 'x-html-editor-tip'
46339         },
46340         justifycenter : {
46341             title: 'Center Text',
46342             text: 'Center text in the editor.',
46343             cls: 'x-html-editor-tip'
46344         },
46345         justifyright : {
46346             title: 'Align Text Right',
46347             text: 'Align text to the right.',
46348             cls: 'x-html-editor-tip'
46349         },
46350         insertunorderedlist : {
46351             title: 'Bullet List',
46352             text: 'Start a bulleted list.',
46353             cls: 'x-html-editor-tip'
46354         },
46355         insertorderedlist : {
46356             title: 'Numbered List',
46357             text: 'Start a numbered list.',
46358             cls: 'x-html-editor-tip'
46359         },
46360         createlink : {
46361             title: 'Hyperlink',
46362             text: 'Make the selected text a hyperlink.',
46363             cls: 'x-html-editor-tip'
46364         },
46365         sourceedit : {
46366             title: 'Source Edit',
46367             text: 'Switch to source editing mode.',
46368             cls: 'x-html-editor-tip'
46369         }
46370     },
46371     // private
46372     onDestroy : function(){
46373         if(this.rendered){
46374             
46375             this.tb.items.each(function(item){
46376                 if(item.menu){
46377                     item.menu.removeAll();
46378                     if(item.menu.el){
46379                         item.menu.el.destroy();
46380                     }
46381                 }
46382                 item.destroy();
46383             });
46384              
46385         }
46386     },
46387     onFirstFocus: function() {
46388         this.tb.items.each(function(item){
46389            item.enable();
46390         });
46391     }
46392 });
46393
46394
46395
46396
46397 // <script type="text/javascript">
46398 /*
46399  * Based on
46400  * Ext JS Library 1.1.1
46401  * Copyright(c) 2006-2007, Ext JS, LLC.
46402  *  
46403  
46404  */
46405
46406  
46407 /**
46408  * @class Roo.form.HtmlEditor.ToolbarContext
46409  * Context Toolbar
46410  * 
46411  * Usage:
46412  *
46413  new Roo.form.HtmlEditor({
46414     ....
46415     toolbars : [
46416         { xtype: 'ToolbarStandard', styles : {} }
46417         { xtype: 'ToolbarContext', disable : {} }
46418     ]
46419 })
46420
46421      
46422  * 
46423  * @config : {Object} disable List of elements to disable.. (not done yet.)
46424  * @config : {Object} styles  Map of styles available.
46425  * 
46426  */
46427
46428 Roo.form.HtmlEditor.ToolbarContext = function(config)
46429 {
46430     
46431     Roo.apply(this, config);
46432     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46433     // dont call parent... till later.
46434     this.styles = this.styles || {};
46435 }
46436
46437  
46438
46439 Roo.form.HtmlEditor.ToolbarContext.types = {
46440     'IMG' : {
46441         width : {
46442             title: "Width",
46443             width: 40
46444         },
46445         height:  {
46446             title: "Height",
46447             width: 40
46448         },
46449         align: {
46450             title: "Align",
46451             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46452             width : 80
46453             
46454         },
46455         border: {
46456             title: "Border",
46457             width: 40
46458         },
46459         alt: {
46460             title: "Alt",
46461             width: 120
46462         },
46463         src : {
46464             title: "Src",
46465             width: 220
46466         }
46467         
46468     },
46469     'A' : {
46470         name : {
46471             title: "Name",
46472             width: 50
46473         },
46474         target:  {
46475             title: "Target",
46476             width: 120
46477         },
46478         href:  {
46479             title: "Href",
46480             width: 220
46481         } // border?
46482         
46483     },
46484     'TABLE' : {
46485         rows : {
46486             title: "Rows",
46487             width: 20
46488         },
46489         cols : {
46490             title: "Cols",
46491             width: 20
46492         },
46493         width : {
46494             title: "Width",
46495             width: 40
46496         },
46497         height : {
46498             title: "Height",
46499             width: 40
46500         },
46501         border : {
46502             title: "Border",
46503             width: 20
46504         }
46505     },
46506     'TD' : {
46507         width : {
46508             title: "Width",
46509             width: 40
46510         },
46511         height : {
46512             title: "Height",
46513             width: 40
46514         },   
46515         align: {
46516             title: "Align",
46517             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46518             width: 80
46519         },
46520         valign: {
46521             title: "Valign",
46522             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46523             width: 80
46524         },
46525         colspan: {
46526             title: "Colspan",
46527             width: 20
46528             
46529         },
46530          'font-family'  : {
46531             title : "Font",
46532             style : 'fontFamily',
46533             displayField: 'display',
46534             optname : 'font-family',
46535             width: 140
46536         }
46537     },
46538     'INPUT' : {
46539         name : {
46540             title: "name",
46541             width: 120
46542         },
46543         value : {
46544             title: "Value",
46545             width: 120
46546         },
46547         width : {
46548             title: "Width",
46549             width: 40
46550         }
46551     },
46552     'LABEL' : {
46553         'for' : {
46554             title: "For",
46555             width: 120
46556         }
46557     },
46558     'TEXTAREA' : {
46559           name : {
46560             title: "name",
46561             width: 120
46562         },
46563         rows : {
46564             title: "Rows",
46565             width: 20
46566         },
46567         cols : {
46568             title: "Cols",
46569             width: 20
46570         }
46571     },
46572     'SELECT' : {
46573         name : {
46574             title: "name",
46575             width: 120
46576         },
46577         selectoptions : {
46578             title: "Options",
46579             width: 200
46580         }
46581     },
46582     
46583     // should we really allow this??
46584     // should this just be 
46585     'BODY' : {
46586         title : {
46587             title: "Title",
46588             width: 200,
46589             disabled : true
46590         }
46591     },
46592     'SPAN' : {
46593         'font-family'  : {
46594             title : "Font",
46595             style : 'fontFamily',
46596             displayField: 'display',
46597             optname : 'font-family',
46598             width: 140
46599         }
46600     },
46601     'DIV' : {
46602         'font-family'  : {
46603             title : "Font",
46604             style : 'fontFamily',
46605             displayField: 'display',
46606             optname : 'font-family',
46607             width: 140
46608         }
46609     },
46610      'P' : {
46611         'font-family'  : {
46612             title : "Font",
46613             style : 'fontFamily',
46614             displayField: 'display',
46615             optname : 'font-family',
46616             width: 140
46617         }
46618     },
46619     
46620     '*' : {
46621         // empty..
46622     }
46623
46624 };
46625
46626 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46627 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46628
46629 Roo.form.HtmlEditor.ToolbarContext.options = {
46630         'font-family'  : [ 
46631                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46632                 [ 'Courier New', 'Courier New'],
46633                 [ 'Tahoma', 'Tahoma'],
46634                 [ 'Times New Roman,serif', 'Times'],
46635                 [ 'Verdana','Verdana' ]
46636         ]
46637 };
46638
46639 // fixme - these need to be configurable..
46640  
46641
46642 //Roo.form.HtmlEditor.ToolbarContext.types
46643
46644
46645 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46646     
46647     tb: false,
46648     
46649     rendered: false,
46650     
46651     editor : false,
46652     editorcore : false,
46653     /**
46654      * @cfg {Object} disable  List of toolbar elements to disable
46655          
46656      */
46657     disable : false,
46658     /**
46659      * @cfg {Object} styles List of styles 
46660      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46661      *
46662      * These must be defined in the page, so they get rendered correctly..
46663      * .headline { }
46664      * TD.underline { }
46665      * 
46666      */
46667     styles : false,
46668     
46669     options: false,
46670     
46671     toolbars : false,
46672     
46673     init : function(editor)
46674     {
46675         this.editor = editor;
46676         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46677         var editorcore = this.editorcore;
46678         
46679         var fid = editorcore.frameId;
46680         var etb = this;
46681         function btn(id, toggle, handler){
46682             var xid = fid + '-'+ id ;
46683             return {
46684                 id : xid,
46685                 cmd : id,
46686                 cls : 'x-btn-icon x-edit-'+id,
46687                 enableToggle:toggle !== false,
46688                 scope: editorcore, // was editor...
46689                 handler:handler||editorcore.relayBtnCmd,
46690                 clickEvent:'mousedown',
46691                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46692                 tabIndex:-1
46693             };
46694         }
46695         // create a new element.
46696         var wdiv = editor.wrap.createChild({
46697                 tag: 'div'
46698             }, editor.wrap.dom.firstChild.nextSibling, true);
46699         
46700         // can we do this more than once??
46701         
46702          // stop form submits
46703       
46704  
46705         // disable everything...
46706         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46707         this.toolbars = {};
46708            
46709         for (var i in  ty) {
46710           
46711             this.toolbars[i] = this.buildToolbar(ty[i],i);
46712         }
46713         this.tb = this.toolbars.BODY;
46714         this.tb.el.show();
46715         this.buildFooter();
46716         this.footer.show();
46717         editor.on('hide', function( ) { this.footer.hide() }, this);
46718         editor.on('show', function( ) { this.footer.show() }, this);
46719         
46720          
46721         this.rendered = true;
46722         
46723         // the all the btns;
46724         editor.on('editorevent', this.updateToolbar, this);
46725         // other toolbars need to implement this..
46726         //editor.on('editmodechange', this.updateToolbar, this);
46727     },
46728     
46729     
46730     
46731     /**
46732      * Protected method that will not generally be called directly. It triggers
46733      * a toolbar update by reading the markup state of the current selection in the editor.
46734      *
46735      * Note you can force an update by calling on('editorevent', scope, false)
46736      */
46737     updateToolbar: function(editor,ev,sel){
46738
46739         //Roo.log(ev);
46740         // capture mouse up - this is handy for selecting images..
46741         // perhaps should go somewhere else...
46742         if(!this.editorcore.activated){
46743              this.editor.onFirstFocus();
46744             return;
46745         }
46746         
46747         
46748         
46749         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
46750         // selectNode - might want to handle IE?
46751         if (ev &&
46752             (ev.type == 'mouseup' || ev.type == 'click' ) &&
46753             ev.target && ev.target.tagName == 'IMG') {
46754             // they have click on an image...
46755             // let's see if we can change the selection...
46756             sel = ev.target;
46757          
46758               var nodeRange = sel.ownerDocument.createRange();
46759             try {
46760                 nodeRange.selectNode(sel);
46761             } catch (e) {
46762                 nodeRange.selectNodeContents(sel);
46763             }
46764             //nodeRange.collapse(true);
46765             var s = this.editorcore.win.getSelection();
46766             s.removeAllRanges();
46767             s.addRange(nodeRange);
46768         }  
46769         
46770       
46771         var updateFooter = sel ? false : true;
46772         
46773         
46774         var ans = this.editorcore.getAllAncestors();
46775         
46776         // pick
46777         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
46778         
46779         if (!sel) { 
46780             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
46781             sel = sel ? sel : this.editorcore.doc.body;
46782             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
46783             
46784         }
46785         // pick a menu that exists..
46786         var tn = sel.tagName.toUpperCase();
46787         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
46788         
46789         tn = sel.tagName.toUpperCase();
46790         
46791         var lastSel = this.tb.selectedNode;
46792         
46793         this.tb.selectedNode = sel;
46794         
46795         // if current menu does not match..
46796         
46797         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
46798                 
46799             this.tb.el.hide();
46800             ///console.log("show: " + tn);
46801             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
46802             this.tb.el.show();
46803             // update name
46804             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
46805             
46806             
46807             // update attributes
46808             if (this.tb.fields) {
46809                 this.tb.fields.each(function(e) {
46810                     if (e.stylename) {
46811                         e.setValue(sel.style[e.stylename]);
46812                         return;
46813                     } 
46814                    e.setValue(sel.getAttribute(e.attrname));
46815                 });
46816             }
46817             
46818             var hasStyles = false;
46819             for(var i in this.styles) {
46820                 hasStyles = true;
46821                 break;
46822             }
46823             
46824             // update styles
46825             if (hasStyles) { 
46826                 var st = this.tb.fields.item(0);
46827                 
46828                 st.store.removeAll();
46829                
46830                 
46831                 var cn = sel.className.split(/\s+/);
46832                 
46833                 var avs = [];
46834                 if (this.styles['*']) {
46835                     
46836                     Roo.each(this.styles['*'], function(v) {
46837                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46838                     });
46839                 }
46840                 if (this.styles[tn]) { 
46841                     Roo.each(this.styles[tn], function(v) {
46842                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
46843                     });
46844                 }
46845                 
46846                 st.store.loadData(avs);
46847                 st.collapse();
46848                 st.setValue(cn);
46849             }
46850             // flag our selected Node.
46851             this.tb.selectedNode = sel;
46852            
46853            
46854             Roo.menu.MenuMgr.hideAll();
46855
46856         }
46857         
46858         if (!updateFooter) {
46859             //this.footDisp.dom.innerHTML = ''; 
46860             return;
46861         }
46862         // update the footer
46863         //
46864         var html = '';
46865         
46866         this.footerEls = ans.reverse();
46867         Roo.each(this.footerEls, function(a,i) {
46868             if (!a) { return; }
46869             html += html.length ? ' &gt; '  :  '';
46870             
46871             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
46872             
46873         });
46874        
46875         // 
46876         var sz = this.footDisp.up('td').getSize();
46877         this.footDisp.dom.style.width = (sz.width -10) + 'px';
46878         this.footDisp.dom.style.marginLeft = '5px';
46879         
46880         this.footDisp.dom.style.overflow = 'hidden';
46881         
46882         this.footDisp.dom.innerHTML = html;
46883             
46884         //this.editorsyncValue();
46885     },
46886      
46887     
46888    
46889        
46890     // private
46891     onDestroy : function(){
46892         if(this.rendered){
46893             
46894             this.tb.items.each(function(item){
46895                 if(item.menu){
46896                     item.menu.removeAll();
46897                     if(item.menu.el){
46898                         item.menu.el.destroy();
46899                     }
46900                 }
46901                 item.destroy();
46902             });
46903              
46904         }
46905     },
46906     onFirstFocus: function() {
46907         // need to do this for all the toolbars..
46908         this.tb.items.each(function(item){
46909            item.enable();
46910         });
46911     },
46912     buildToolbar: function(tlist, nm)
46913     {
46914         var editor = this.editor;
46915         var editorcore = this.editorcore;
46916          // create a new element.
46917         var wdiv = editor.wrap.createChild({
46918                 tag: 'div'
46919             }, editor.wrap.dom.firstChild.nextSibling, true);
46920         
46921        
46922         var tb = new Roo.Toolbar(wdiv);
46923         // add the name..
46924         
46925         tb.add(nm+ ":&nbsp;");
46926         
46927         var styles = [];
46928         for(var i in this.styles) {
46929             styles.push(i);
46930         }
46931         
46932         // styles...
46933         if (styles && styles.length) {
46934             
46935             // this needs a multi-select checkbox...
46936             tb.addField( new Roo.form.ComboBox({
46937                 store: new Roo.data.SimpleStore({
46938                     id : 'val',
46939                     fields: ['val', 'selected'],
46940                     data : [] 
46941                 }),
46942                 name : '-roo-edit-className',
46943                 attrname : 'className',
46944                 displayField: 'val',
46945                 typeAhead: false,
46946                 mode: 'local',
46947                 editable : false,
46948                 triggerAction: 'all',
46949                 emptyText:'Select Style',
46950                 selectOnFocus:true,
46951                 width: 130,
46952                 listeners : {
46953                     'select': function(c, r, i) {
46954                         // initial support only for on class per el..
46955                         tb.selectedNode.className =  r ? r.get('val') : '';
46956                         editorcore.syncValue();
46957                     }
46958                 }
46959     
46960             }));
46961         }
46962         
46963         var tbc = Roo.form.HtmlEditor.ToolbarContext;
46964         var tbops = tbc.options;
46965         
46966         for (var i in tlist) {
46967             
46968             var item = tlist[i];
46969             tb.add(item.title + ":&nbsp;");
46970             
46971             
46972             //optname == used so you can configure the options available..
46973             var opts = item.opts ? item.opts : false;
46974             if (item.optname) {
46975                 opts = tbops[item.optname];
46976            
46977             }
46978             
46979             if (opts) {
46980                 // opts == pulldown..
46981                 tb.addField( new Roo.form.ComboBox({
46982                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
46983                         id : 'val',
46984                         fields: ['val', 'display'],
46985                         data : opts  
46986                     }),
46987                     name : '-roo-edit-' + i,
46988                     attrname : i,
46989                     stylename : item.style ? item.style : false,
46990                     displayField: item.displayField ? item.displayField : 'val',
46991                     valueField :  'val',
46992                     typeAhead: false,
46993                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
46994                     editable : false,
46995                     triggerAction: 'all',
46996                     emptyText:'Select',
46997                     selectOnFocus:true,
46998                     width: item.width ? item.width  : 130,
46999                     listeners : {
47000                         'select': function(c, r, i) {
47001                             if (c.stylename) {
47002                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47003                                 return;
47004                             }
47005                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47006                         }
47007                     }
47008
47009                 }));
47010                 continue;
47011                     
47012                  
47013                 
47014                 tb.addField( new Roo.form.TextField({
47015                     name: i,
47016                     width: 100,
47017                     //allowBlank:false,
47018                     value: ''
47019                 }));
47020                 continue;
47021             }
47022             tb.addField( new Roo.form.TextField({
47023                 name: '-roo-edit-' + i,
47024                 attrname : i,
47025                 
47026                 width: item.width,
47027                 //allowBlank:true,
47028                 value: '',
47029                 listeners: {
47030                     'change' : function(f, nv, ov) {
47031                         tb.selectedNode.setAttribute(f.attrname, nv);
47032                         editorcore.syncValue();
47033                     }
47034                 }
47035             }));
47036              
47037         }
47038         
47039         var _this = this;
47040         
47041         if(nm == 'BODY'){
47042             tb.addSeparator();
47043         
47044             tb.addButton( {
47045                 text: 'Stylesheets',
47046
47047                 listeners : {
47048                     click : function ()
47049                     {
47050                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47051                     }
47052                 }
47053             });
47054         }
47055         
47056         tb.addFill();
47057         tb.addButton( {
47058             text: 'Remove Tag',
47059     
47060             listeners : {
47061                 click : function ()
47062                 {
47063                     // remove
47064                     // undo does not work.
47065                      
47066                     var sn = tb.selectedNode;
47067                     
47068                     var pn = sn.parentNode;
47069                     
47070                     var stn =  sn.childNodes[0];
47071                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47072                     while (sn.childNodes.length) {
47073                         var node = sn.childNodes[0];
47074                         sn.removeChild(node);
47075                         //Roo.log(node);
47076                         pn.insertBefore(node, sn);
47077                         
47078                     }
47079                     pn.removeChild(sn);
47080                     var range = editorcore.createRange();
47081         
47082                     range.setStart(stn,0);
47083                     range.setEnd(en,0); //????
47084                     //range.selectNode(sel);
47085                     
47086                     
47087                     var selection = editorcore.getSelection();
47088                     selection.removeAllRanges();
47089                     selection.addRange(range);
47090                     
47091                     
47092                     
47093                     //_this.updateToolbar(null, null, pn);
47094                     _this.updateToolbar(null, null, null);
47095                     _this.footDisp.dom.innerHTML = ''; 
47096                 }
47097             }
47098             
47099                     
47100                 
47101             
47102         });
47103         
47104         
47105         tb.el.on('click', function(e){
47106             e.preventDefault(); // what does this do?
47107         });
47108         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47109         tb.el.hide();
47110         tb.name = nm;
47111         // dont need to disable them... as they will get hidden
47112         return tb;
47113          
47114         
47115     },
47116     buildFooter : function()
47117     {
47118         
47119         var fel = this.editor.wrap.createChild();
47120         this.footer = new Roo.Toolbar(fel);
47121         // toolbar has scrolly on left / right?
47122         var footDisp= new Roo.Toolbar.Fill();
47123         var _t = this;
47124         this.footer.add(
47125             {
47126                 text : '&lt;',
47127                 xtype: 'Button',
47128                 handler : function() {
47129                     _t.footDisp.scrollTo('left',0,true)
47130                 }
47131             }
47132         );
47133         this.footer.add( footDisp );
47134         this.footer.add( 
47135             {
47136                 text : '&gt;',
47137                 xtype: 'Button',
47138                 handler : function() {
47139                     // no animation..
47140                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47141                 }
47142             }
47143         );
47144         var fel = Roo.get(footDisp.el);
47145         fel.addClass('x-editor-context');
47146         this.footDispWrap = fel; 
47147         this.footDispWrap.overflow  = 'hidden';
47148         
47149         this.footDisp = fel.createChild();
47150         this.footDispWrap.on('click', this.onContextClick, this)
47151         
47152         
47153     },
47154     onContextClick : function (ev,dom)
47155     {
47156         ev.preventDefault();
47157         var  cn = dom.className;
47158         //Roo.log(cn);
47159         if (!cn.match(/x-ed-loc-/)) {
47160             return;
47161         }
47162         var n = cn.split('-').pop();
47163         var ans = this.footerEls;
47164         var sel = ans[n];
47165         
47166          // pick
47167         var range = this.editorcore.createRange();
47168         
47169         range.selectNodeContents(sel);
47170         //range.selectNode(sel);
47171         
47172         
47173         var selection = this.editorcore.getSelection();
47174         selection.removeAllRanges();
47175         selection.addRange(range);
47176         
47177         
47178         
47179         this.updateToolbar(null, null, sel);
47180         
47181         
47182     }
47183     
47184     
47185     
47186     
47187     
47188 });
47189
47190
47191
47192
47193
47194 /*
47195  * Based on:
47196  * Ext JS Library 1.1.1
47197  * Copyright(c) 2006-2007, Ext JS, LLC.
47198  *
47199  * Originally Released Under LGPL - original licence link has changed is not relivant.
47200  *
47201  * Fork - LGPL
47202  * <script type="text/javascript">
47203  */
47204  
47205 /**
47206  * @class Roo.form.BasicForm
47207  * @extends Roo.util.Observable
47208  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47209  * @constructor
47210  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47211  * @param {Object} config Configuration options
47212  */
47213 Roo.form.BasicForm = function(el, config){
47214     this.allItems = [];
47215     this.childForms = [];
47216     Roo.apply(this, config);
47217     /*
47218      * The Roo.form.Field items in this form.
47219      * @type MixedCollection
47220      */
47221      
47222      
47223     this.items = new Roo.util.MixedCollection(false, function(o){
47224         return o.id || (o.id = Roo.id());
47225     });
47226     this.addEvents({
47227         /**
47228          * @event beforeaction
47229          * Fires before any action is performed. Return false to cancel the action.
47230          * @param {Form} this
47231          * @param {Action} action The action to be performed
47232          */
47233         beforeaction: true,
47234         /**
47235          * @event actionfailed
47236          * Fires when an action fails.
47237          * @param {Form} this
47238          * @param {Action} action The action that failed
47239          */
47240         actionfailed : true,
47241         /**
47242          * @event actioncomplete
47243          * Fires when an action is completed.
47244          * @param {Form} this
47245          * @param {Action} action The action that completed
47246          */
47247         actioncomplete : true
47248     });
47249     if(el){
47250         this.initEl(el);
47251     }
47252     Roo.form.BasicForm.superclass.constructor.call(this);
47253     
47254     Roo.form.BasicForm.popover.apply();
47255 };
47256
47257 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47258     /**
47259      * @cfg {String} method
47260      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47261      */
47262     /**
47263      * @cfg {DataReader} reader
47264      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47265      * This is optional as there is built-in support for processing JSON.
47266      */
47267     /**
47268      * @cfg {DataReader} errorReader
47269      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47270      * This is completely optional as there is built-in support for processing JSON.
47271      */
47272     /**
47273      * @cfg {String} url
47274      * The URL to use for form actions if one isn't supplied in the action options.
47275      */
47276     /**
47277      * @cfg {Boolean} fileUpload
47278      * Set to true if this form is a file upload.
47279      */
47280      
47281     /**
47282      * @cfg {Object} baseParams
47283      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47284      */
47285      /**
47286      
47287     /**
47288      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47289      */
47290     timeout: 30,
47291
47292     // private
47293     activeAction : null,
47294
47295     /**
47296      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47297      * or setValues() data instead of when the form was first created.
47298      */
47299     trackResetOnLoad : false,
47300     
47301     
47302     /**
47303      * childForms - used for multi-tab forms
47304      * @type {Array}
47305      */
47306     childForms : false,
47307     
47308     /**
47309      * allItems - full list of fields.
47310      * @type {Array}
47311      */
47312     allItems : false,
47313     
47314     /**
47315      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47316      * element by passing it or its id or mask the form itself by passing in true.
47317      * @type Mixed
47318      */
47319     waitMsgTarget : false,
47320     
47321     /**
47322      * @type Boolean
47323      */
47324     disableMask : false,
47325     
47326     /**
47327      * @cfg {Boolean} errorMask (true|false) default false
47328      */
47329     errorMask : false,
47330     
47331     /**
47332      * @cfg {Number} maskOffset Default 100
47333      */
47334     maskOffset : 100,
47335
47336     // private
47337     initEl : function(el){
47338         this.el = Roo.get(el);
47339         this.id = this.el.id || Roo.id();
47340         this.el.on('submit', this.onSubmit, this);
47341         this.el.addClass('x-form');
47342     },
47343
47344     // private
47345     onSubmit : function(e){
47346         e.stopEvent();
47347     },
47348
47349     /**
47350      * Returns true if client-side validation on the form is successful.
47351      * @return Boolean
47352      */
47353     isValid : function(){
47354         var valid = true;
47355         var target = false;
47356         this.items.each(function(f){
47357             if(f.validate()){
47358                 return;
47359             }
47360             
47361             valid = false;
47362                 
47363             if(!target && f.el.isVisible(true)){
47364                 target = f;
47365             }
47366         });
47367         
47368         if(this.errorMask && !valid){
47369             Roo.form.BasicForm.popover.mask(this, target);
47370         }
47371         
47372         return valid;
47373     },
47374
47375     /**
47376      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47377      * @return Boolean
47378      */
47379     isDirty : function(){
47380         var dirty = false;
47381         this.items.each(function(f){
47382            if(f.isDirty()){
47383                dirty = true;
47384                return false;
47385            }
47386         });
47387         return dirty;
47388     },
47389     
47390     /**
47391      * Returns true if any fields in this form have changed since their original load. (New version)
47392      * @return Boolean
47393      */
47394     
47395     hasChanged : function()
47396     {
47397         var dirty = false;
47398         this.items.each(function(f){
47399            if(f.hasChanged()){
47400                dirty = true;
47401                return false;
47402            }
47403         });
47404         return dirty;
47405         
47406     },
47407     /**
47408      * Resets all hasChanged to 'false' -
47409      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47410      * So hasChanged storage is only to be used for this purpose
47411      * @return Boolean
47412      */
47413     resetHasChanged : function()
47414     {
47415         this.items.each(function(f){
47416            f.resetHasChanged();
47417         });
47418         
47419     },
47420     
47421     
47422     /**
47423      * Performs a predefined action (submit or load) or custom actions you define on this form.
47424      * @param {String} actionName The name of the action type
47425      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47426      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47427      * accept other config options):
47428      * <pre>
47429 Property          Type             Description
47430 ----------------  ---------------  ----------------------------------------------------------------------------------
47431 url               String           The url for the action (defaults to the form's url)
47432 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47433 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47434 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47435                                    validate the form on the client (defaults to false)
47436      * </pre>
47437      * @return {BasicForm} this
47438      */
47439     doAction : function(action, options){
47440         if(typeof action == 'string'){
47441             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47442         }
47443         if(this.fireEvent('beforeaction', this, action) !== false){
47444             this.beforeAction(action);
47445             action.run.defer(100, action);
47446         }
47447         return this;
47448     },
47449
47450     /**
47451      * Shortcut to do a submit action.
47452      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47453      * @return {BasicForm} this
47454      */
47455     submit : function(options){
47456         this.doAction('submit', options);
47457         return this;
47458     },
47459
47460     /**
47461      * Shortcut to do a load action.
47462      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47463      * @return {BasicForm} this
47464      */
47465     load : function(options){
47466         this.doAction('load', options);
47467         return this;
47468     },
47469
47470     /**
47471      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47472      * @param {Record} record The record to edit
47473      * @return {BasicForm} this
47474      */
47475     updateRecord : function(record){
47476         record.beginEdit();
47477         var fs = record.fields;
47478         fs.each(function(f){
47479             var field = this.findField(f.name);
47480             if(field){
47481                 record.set(f.name, field.getValue());
47482             }
47483         }, this);
47484         record.endEdit();
47485         return this;
47486     },
47487
47488     /**
47489      * Loads an Roo.data.Record into this form.
47490      * @param {Record} record The record to load
47491      * @return {BasicForm} this
47492      */
47493     loadRecord : function(record){
47494         this.setValues(record.data);
47495         return this;
47496     },
47497
47498     // private
47499     beforeAction : function(action){
47500         var o = action.options;
47501         
47502         if(!this.disableMask) {
47503             if(this.waitMsgTarget === true){
47504                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47505             }else if(this.waitMsgTarget){
47506                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47507                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47508             }else {
47509                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47510             }
47511         }
47512         
47513          
47514     },
47515
47516     // private
47517     afterAction : function(action, success){
47518         this.activeAction = null;
47519         var o = action.options;
47520         
47521         if(!this.disableMask) {
47522             if(this.waitMsgTarget === true){
47523                 this.el.unmask();
47524             }else if(this.waitMsgTarget){
47525                 this.waitMsgTarget.unmask();
47526             }else{
47527                 Roo.MessageBox.updateProgress(1);
47528                 Roo.MessageBox.hide();
47529             }
47530         }
47531         
47532         if(success){
47533             if(o.reset){
47534                 this.reset();
47535             }
47536             Roo.callback(o.success, o.scope, [this, action]);
47537             this.fireEvent('actioncomplete', this, action);
47538             
47539         }else{
47540             
47541             // failure condition..
47542             // we have a scenario where updates need confirming.
47543             // eg. if a locking scenario exists..
47544             // we look for { errors : { needs_confirm : true }} in the response.
47545             if (
47546                 (typeof(action.result) != 'undefined')  &&
47547                 (typeof(action.result.errors) != 'undefined')  &&
47548                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47549            ){
47550                 var _t = this;
47551                 Roo.MessageBox.confirm(
47552                     "Change requires confirmation",
47553                     action.result.errorMsg,
47554                     function(r) {
47555                         if (r != 'yes') {
47556                             return;
47557                         }
47558                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47559                     }
47560                     
47561                 );
47562                 
47563                 
47564                 
47565                 return;
47566             }
47567             
47568             Roo.callback(o.failure, o.scope, [this, action]);
47569             // show an error message if no failed handler is set..
47570             if (!this.hasListener('actionfailed')) {
47571                 Roo.MessageBox.alert("Error",
47572                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47573                         action.result.errorMsg :
47574                         "Saving Failed, please check your entries or try again"
47575                 );
47576             }
47577             
47578             this.fireEvent('actionfailed', this, action);
47579         }
47580         
47581     },
47582
47583     /**
47584      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47585      * @param {String} id The value to search for
47586      * @return Field
47587      */
47588     findField : function(id){
47589         var field = this.items.get(id);
47590         if(!field){
47591             this.items.each(function(f){
47592                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47593                     field = f;
47594                     return false;
47595                 }
47596             });
47597         }
47598         return field || null;
47599     },
47600
47601     /**
47602      * Add a secondary form to this one, 
47603      * Used to provide tabbed forms. One form is primary, with hidden values 
47604      * which mirror the elements from the other forms.
47605      * 
47606      * @param {Roo.form.Form} form to add.
47607      * 
47608      */
47609     addForm : function(form)
47610     {
47611        
47612         if (this.childForms.indexOf(form) > -1) {
47613             // already added..
47614             return;
47615         }
47616         this.childForms.push(form);
47617         var n = '';
47618         Roo.each(form.allItems, function (fe) {
47619             
47620             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47621             if (this.findField(n)) { // already added..
47622                 return;
47623             }
47624             var add = new Roo.form.Hidden({
47625                 name : n
47626             });
47627             add.render(this.el);
47628             
47629             this.add( add );
47630         }, this);
47631         
47632     },
47633     /**
47634      * Mark fields in this form invalid in bulk.
47635      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47636      * @return {BasicForm} this
47637      */
47638     markInvalid : function(errors){
47639         if(errors instanceof Array){
47640             for(var i = 0, len = errors.length; i < len; i++){
47641                 var fieldError = errors[i];
47642                 var f = this.findField(fieldError.id);
47643                 if(f){
47644                     f.markInvalid(fieldError.msg);
47645                 }
47646             }
47647         }else{
47648             var field, id;
47649             for(id in errors){
47650                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
47651                     field.markInvalid(errors[id]);
47652                 }
47653             }
47654         }
47655         Roo.each(this.childForms || [], function (f) {
47656             f.markInvalid(errors);
47657         });
47658         
47659         return this;
47660     },
47661
47662     /**
47663      * Set values for fields in this form in bulk.
47664      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
47665      * @return {BasicForm} this
47666      */
47667     setValues : function(values){
47668         if(values instanceof Array){ // array of objects
47669             for(var i = 0, len = values.length; i < len; i++){
47670                 var v = values[i];
47671                 var f = this.findField(v.id);
47672                 if(f){
47673                     f.setValue(v.value);
47674                     if(this.trackResetOnLoad){
47675                         f.originalValue = f.getValue();
47676                     }
47677                 }
47678             }
47679         }else{ // object hash
47680             var field, id;
47681             for(id in values){
47682                 if(typeof values[id] != 'function' && (field = this.findField(id))){
47683                     
47684                     if (field.setFromData && 
47685                         field.valueField && 
47686                         field.displayField &&
47687                         // combos' with local stores can 
47688                         // be queried via setValue()
47689                         // to set their value..
47690                         (field.store && !field.store.isLocal)
47691                         ) {
47692                         // it's a combo
47693                         var sd = { };
47694                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
47695                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
47696                         field.setFromData(sd);
47697                         
47698                     } else {
47699                         field.setValue(values[id]);
47700                     }
47701                     
47702                     
47703                     if(this.trackResetOnLoad){
47704                         field.originalValue = field.getValue();
47705                     }
47706                 }
47707             }
47708         }
47709         this.resetHasChanged();
47710         
47711         
47712         Roo.each(this.childForms || [], function (f) {
47713             f.setValues(values);
47714             f.resetHasChanged();
47715         });
47716                 
47717         return this;
47718     },
47719  
47720     /**
47721      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
47722      * they are returned as an array.
47723      * @param {Boolean} asString
47724      * @return {Object}
47725      */
47726     getValues : function(asString){
47727         if (this.childForms) {
47728             // copy values from the child forms
47729             Roo.each(this.childForms, function (f) {
47730                 this.setValues(f.getValues());
47731             }, this);
47732         }
47733         
47734         // use formdata
47735         if (typeof(FormData) != 'undefined' && asString !== true) {
47736             var fd = (new FormData(this.el.dom)).entries();
47737             var ret = {};
47738             var ent = fd.next();
47739             while (!ent.done) {
47740                 ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
47741                 ent = fd.next();
47742             };
47743             return ret;
47744         }
47745         
47746         
47747         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
47748         if(asString === true){
47749             return fs;
47750         }
47751         return Roo.urlDecode(fs);
47752     },
47753     
47754     /**
47755      * Returns the fields in this form as an object with key/value pairs. 
47756      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
47757      * @return {Object}
47758      */
47759     getFieldValues : function(with_hidden)
47760     {
47761         if (this.childForms) {
47762             // copy values from the child forms
47763             // should this call getFieldValues - probably not as we do not currently copy
47764             // hidden fields when we generate..
47765             Roo.each(this.childForms, function (f) {
47766                 this.setValues(f.getValues());
47767             }, this);
47768         }
47769         
47770         var ret = {};
47771         this.items.each(function(f){
47772             if (!f.getName()) {
47773                 return;
47774             }
47775             var v = f.getValue();
47776             if (f.inputType =='radio') {
47777                 if (typeof(ret[f.getName()]) == 'undefined') {
47778                     ret[f.getName()] = ''; // empty..
47779                 }
47780                 
47781                 if (!f.el.dom.checked) {
47782                     return;
47783                     
47784                 }
47785                 v = f.el.dom.value;
47786                 
47787             }
47788             
47789             // not sure if this supported any more..
47790             if ((typeof(v) == 'object') && f.getRawValue) {
47791                 v = f.getRawValue() ; // dates..
47792             }
47793             // combo boxes where name != hiddenName...
47794             if (f.name != f.getName()) {
47795                 ret[f.name] = f.getRawValue();
47796             }
47797             ret[f.getName()] = v;
47798         });
47799         
47800         return ret;
47801     },
47802
47803     /**
47804      * Clears all invalid messages in this form.
47805      * @return {BasicForm} this
47806      */
47807     clearInvalid : function(){
47808         this.items.each(function(f){
47809            f.clearInvalid();
47810         });
47811         
47812         Roo.each(this.childForms || [], function (f) {
47813             f.clearInvalid();
47814         });
47815         
47816         
47817         return this;
47818     },
47819
47820     /**
47821      * Resets this form.
47822      * @return {BasicForm} this
47823      */
47824     reset : function(){
47825         this.items.each(function(f){
47826             f.reset();
47827         });
47828         
47829         Roo.each(this.childForms || [], function (f) {
47830             f.reset();
47831         });
47832         this.resetHasChanged();
47833         
47834         return this;
47835     },
47836
47837     /**
47838      * Add Roo.form components to this form.
47839      * @param {Field} field1
47840      * @param {Field} field2 (optional)
47841      * @param {Field} etc (optional)
47842      * @return {BasicForm} this
47843      */
47844     add : function(){
47845         this.items.addAll(Array.prototype.slice.call(arguments, 0));
47846         return this;
47847     },
47848
47849
47850     /**
47851      * Removes a field from the items collection (does NOT remove its markup).
47852      * @param {Field} field
47853      * @return {BasicForm} this
47854      */
47855     remove : function(field){
47856         this.items.remove(field);
47857         return this;
47858     },
47859
47860     /**
47861      * Looks at the fields in this form, checks them for an id attribute,
47862      * and calls applyTo on the existing dom element with that id.
47863      * @return {BasicForm} this
47864      */
47865     render : function(){
47866         this.items.each(function(f){
47867             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
47868                 f.applyTo(f.id);
47869             }
47870         });
47871         return this;
47872     },
47873
47874     /**
47875      * Calls {@link Ext#apply} for all fields in this form with the passed object.
47876      * @param {Object} values
47877      * @return {BasicForm} this
47878      */
47879     applyToFields : function(o){
47880         this.items.each(function(f){
47881            Roo.apply(f, o);
47882         });
47883         return this;
47884     },
47885
47886     /**
47887      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
47888      * @param {Object} values
47889      * @return {BasicForm} this
47890      */
47891     applyIfToFields : function(o){
47892         this.items.each(function(f){
47893            Roo.applyIf(f, o);
47894         });
47895         return this;
47896     }
47897 });
47898
47899 // back compat
47900 Roo.BasicForm = Roo.form.BasicForm;
47901
47902 Roo.apply(Roo.form.BasicForm, {
47903     
47904     popover : {
47905         
47906         padding : 5,
47907         
47908         isApplied : false,
47909         
47910         isMasked : false,
47911         
47912         form : false,
47913         
47914         target : false,
47915         
47916         intervalID : false,
47917         
47918         maskEl : false,
47919         
47920         apply : function()
47921         {
47922             if(this.isApplied){
47923                 return;
47924             }
47925             
47926             this.maskEl = {
47927                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
47928                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
47929                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
47930                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
47931             };
47932             
47933             this.maskEl.top.enableDisplayMode("block");
47934             this.maskEl.left.enableDisplayMode("block");
47935             this.maskEl.bottom.enableDisplayMode("block");
47936             this.maskEl.right.enableDisplayMode("block");
47937             
47938             Roo.get(document.body).on('click', function(){
47939                 this.unmask();
47940             }, this);
47941             
47942             Roo.get(document.body).on('touchstart', function(){
47943                 this.unmask();
47944             }, this);
47945             
47946             this.isApplied = true
47947         },
47948         
47949         mask : function(form, target)
47950         {
47951             this.form = form;
47952             
47953             this.target = target;
47954             
47955             if(!this.form.errorMask || !target.el){
47956                 return;
47957             }
47958             
47959             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
47960             
47961             var ot = this.target.el.calcOffsetsTo(scrollable);
47962             
47963             var scrollTo = ot[1] - this.form.maskOffset;
47964             
47965             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
47966             
47967             scrollable.scrollTo('top', scrollTo);
47968             
47969             var el = this.target.wrap || this.target.el;
47970             
47971             var box = el.getBox();
47972             
47973             this.maskEl.top.setStyle('position', 'absolute');
47974             this.maskEl.top.setStyle('z-index', 10000);
47975             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
47976             this.maskEl.top.setLeft(0);
47977             this.maskEl.top.setTop(0);
47978             this.maskEl.top.show();
47979             
47980             this.maskEl.left.setStyle('position', 'absolute');
47981             this.maskEl.left.setStyle('z-index', 10000);
47982             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
47983             this.maskEl.left.setLeft(0);
47984             this.maskEl.left.setTop(box.y - this.padding);
47985             this.maskEl.left.show();
47986
47987             this.maskEl.bottom.setStyle('position', 'absolute');
47988             this.maskEl.bottom.setStyle('z-index', 10000);
47989             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
47990             this.maskEl.bottom.setLeft(0);
47991             this.maskEl.bottom.setTop(box.bottom + this.padding);
47992             this.maskEl.bottom.show();
47993
47994             this.maskEl.right.setStyle('position', 'absolute');
47995             this.maskEl.right.setStyle('z-index', 10000);
47996             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
47997             this.maskEl.right.setLeft(box.right + this.padding);
47998             this.maskEl.right.setTop(box.y - this.padding);
47999             this.maskEl.right.show();
48000
48001             this.intervalID = window.setInterval(function() {
48002                 Roo.form.BasicForm.popover.unmask();
48003             }, 10000);
48004
48005             window.onwheel = function(){ return false;};
48006             
48007             (function(){ this.isMasked = true; }).defer(500, this);
48008             
48009         },
48010         
48011         unmask : function()
48012         {
48013             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48014                 return;
48015             }
48016             
48017             this.maskEl.top.setStyle('position', 'absolute');
48018             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48019             this.maskEl.top.hide();
48020
48021             this.maskEl.left.setStyle('position', 'absolute');
48022             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48023             this.maskEl.left.hide();
48024
48025             this.maskEl.bottom.setStyle('position', 'absolute');
48026             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48027             this.maskEl.bottom.hide();
48028
48029             this.maskEl.right.setStyle('position', 'absolute');
48030             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48031             this.maskEl.right.hide();
48032             
48033             window.onwheel = function(){ return true;};
48034             
48035             if(this.intervalID){
48036                 window.clearInterval(this.intervalID);
48037                 this.intervalID = false;
48038             }
48039             
48040             this.isMasked = false;
48041             
48042         }
48043         
48044     }
48045     
48046 });/*
48047  * Based on:
48048  * Ext JS Library 1.1.1
48049  * Copyright(c) 2006-2007, Ext JS, LLC.
48050  *
48051  * Originally Released Under LGPL - original licence link has changed is not relivant.
48052  *
48053  * Fork - LGPL
48054  * <script type="text/javascript">
48055  */
48056
48057 /**
48058  * @class Roo.form.Form
48059  * @extends Roo.form.BasicForm
48060  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48061  * @constructor
48062  * @param {Object} config Configuration options
48063  */
48064 Roo.form.Form = function(config){
48065     var xitems =  [];
48066     if (config.items) {
48067         xitems = config.items;
48068         delete config.items;
48069     }
48070    
48071     
48072     Roo.form.Form.superclass.constructor.call(this, null, config);
48073     this.url = this.url || this.action;
48074     if(!this.root){
48075         this.root = new Roo.form.Layout(Roo.applyIf({
48076             id: Roo.id()
48077         }, config));
48078     }
48079     this.active = this.root;
48080     /**
48081      * Array of all the buttons that have been added to this form via {@link addButton}
48082      * @type Array
48083      */
48084     this.buttons = [];
48085     this.allItems = [];
48086     this.addEvents({
48087         /**
48088          * @event clientvalidation
48089          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48090          * @param {Form} this
48091          * @param {Boolean} valid true if the form has passed client-side validation
48092          */
48093         clientvalidation: true,
48094         /**
48095          * @event rendered
48096          * Fires when the form is rendered
48097          * @param {Roo.form.Form} form
48098          */
48099         rendered : true
48100     });
48101     
48102     if (this.progressUrl) {
48103             // push a hidden field onto the list of fields..
48104             this.addxtype( {
48105                     xns: Roo.form, 
48106                     xtype : 'Hidden', 
48107                     name : 'UPLOAD_IDENTIFIER' 
48108             });
48109         }
48110         
48111     
48112     Roo.each(xitems, this.addxtype, this);
48113     
48114 };
48115
48116 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48117     /**
48118      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48119      */
48120     /**
48121      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48122      */
48123     /**
48124      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48125      */
48126     buttonAlign:'center',
48127
48128     /**
48129      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48130      */
48131     minButtonWidth:75,
48132
48133     /**
48134      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48135      * This property cascades to child containers if not set.
48136      */
48137     labelAlign:'left',
48138
48139     /**
48140      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48141      * fires a looping event with that state. This is required to bind buttons to the valid
48142      * state using the config value formBind:true on the button.
48143      */
48144     monitorValid : false,
48145
48146     /**
48147      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48148      */
48149     monitorPoll : 200,
48150     
48151     /**
48152      * @cfg {String} progressUrl - Url to return progress data 
48153      */
48154     
48155     progressUrl : false,
48156     /**
48157      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48158      * sending a formdata with extra parameters - eg uploaded elements.
48159      */
48160     
48161     formData : false,
48162     
48163     /**
48164      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48165      * fields are added and the column is closed. If no fields are passed the column remains open
48166      * until end() is called.
48167      * @param {Object} config The config to pass to the column
48168      * @param {Field} field1 (optional)
48169      * @param {Field} field2 (optional)
48170      * @param {Field} etc (optional)
48171      * @return Column The column container object
48172      */
48173     column : function(c){
48174         var col = new Roo.form.Column(c);
48175         this.start(col);
48176         if(arguments.length > 1){ // duplicate code required because of Opera
48177             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48178             this.end();
48179         }
48180         return col;
48181     },
48182
48183     /**
48184      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48185      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48186      * until end() is called.
48187      * @param {Object} config The config to pass to the fieldset
48188      * @param {Field} field1 (optional)
48189      * @param {Field} field2 (optional)
48190      * @param {Field} etc (optional)
48191      * @return FieldSet The fieldset container object
48192      */
48193     fieldset : function(c){
48194         var fs = new Roo.form.FieldSet(c);
48195         this.start(fs);
48196         if(arguments.length > 1){ // duplicate code required because of Opera
48197             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48198             this.end();
48199         }
48200         return fs;
48201     },
48202
48203     /**
48204      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48205      * fields are added and the container is closed. If no fields are passed the container remains open
48206      * until end() is called.
48207      * @param {Object} config The config to pass to the Layout
48208      * @param {Field} field1 (optional)
48209      * @param {Field} field2 (optional)
48210      * @param {Field} etc (optional)
48211      * @return Layout The container object
48212      */
48213     container : function(c){
48214         var l = new Roo.form.Layout(c);
48215         this.start(l);
48216         if(arguments.length > 1){ // duplicate code required because of Opera
48217             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48218             this.end();
48219         }
48220         return l;
48221     },
48222
48223     /**
48224      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48225      * @param {Object} container A Roo.form.Layout or subclass of Layout
48226      * @return {Form} this
48227      */
48228     start : function(c){
48229         // cascade label info
48230         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48231         this.active.stack.push(c);
48232         c.ownerCt = this.active;
48233         this.active = c;
48234         return this;
48235     },
48236
48237     /**
48238      * Closes the current open container
48239      * @return {Form} this
48240      */
48241     end : function(){
48242         if(this.active == this.root){
48243             return this;
48244         }
48245         this.active = this.active.ownerCt;
48246         return this;
48247     },
48248
48249     /**
48250      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48251      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48252      * as the label of the field.
48253      * @param {Field} field1
48254      * @param {Field} field2 (optional)
48255      * @param {Field} etc. (optional)
48256      * @return {Form} this
48257      */
48258     add : function(){
48259         this.active.stack.push.apply(this.active.stack, arguments);
48260         this.allItems.push.apply(this.allItems,arguments);
48261         var r = [];
48262         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48263             if(a[i].isFormField){
48264                 r.push(a[i]);
48265             }
48266         }
48267         if(r.length > 0){
48268             Roo.form.Form.superclass.add.apply(this, r);
48269         }
48270         return this;
48271     },
48272     
48273
48274     
48275     
48276     
48277      /**
48278      * Find any element that has been added to a form, using it's ID or name
48279      * This can include framesets, columns etc. along with regular fields..
48280      * @param {String} id - id or name to find.
48281      
48282      * @return {Element} e - or false if nothing found.
48283      */
48284     findbyId : function(id)
48285     {
48286         var ret = false;
48287         if (!id) {
48288             return ret;
48289         }
48290         Roo.each(this.allItems, function(f){
48291             if (f.id == id || f.name == id ){
48292                 ret = f;
48293                 return false;
48294             }
48295         });
48296         return ret;
48297     },
48298
48299     
48300     
48301     /**
48302      * Render this form into the passed container. This should only be called once!
48303      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48304      * @return {Form} this
48305      */
48306     render : function(ct)
48307     {
48308         
48309         
48310         
48311         ct = Roo.get(ct);
48312         var o = this.autoCreate || {
48313             tag: 'form',
48314             method : this.method || 'POST',
48315             id : this.id || Roo.id()
48316         };
48317         this.initEl(ct.createChild(o));
48318
48319         this.root.render(this.el);
48320         
48321        
48322              
48323         this.items.each(function(f){
48324             f.render('x-form-el-'+f.id);
48325         });
48326
48327         if(this.buttons.length > 0){
48328             // tables are required to maintain order and for correct IE layout
48329             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48330                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48331                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48332             }}, null, true);
48333             var tr = tb.getElementsByTagName('tr')[0];
48334             for(var i = 0, len = this.buttons.length; i < len; i++) {
48335                 var b = this.buttons[i];
48336                 var td = document.createElement('td');
48337                 td.className = 'x-form-btn-td';
48338                 b.render(tr.appendChild(td));
48339             }
48340         }
48341         if(this.monitorValid){ // initialize after render
48342             this.startMonitoring();
48343         }
48344         this.fireEvent('rendered', this);
48345         return this;
48346     },
48347
48348     /**
48349      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48350      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48351      * object or a valid Roo.DomHelper element config
48352      * @param {Function} handler The function called when the button is clicked
48353      * @param {Object} scope (optional) The scope of the handler function
48354      * @return {Roo.Button}
48355      */
48356     addButton : function(config, handler, scope){
48357         var bc = {
48358             handler: handler,
48359             scope: scope,
48360             minWidth: this.minButtonWidth,
48361             hideParent:true
48362         };
48363         if(typeof config == "string"){
48364             bc.text = config;
48365         }else{
48366             Roo.apply(bc, config);
48367         }
48368         var btn = new Roo.Button(null, bc);
48369         this.buttons.push(btn);
48370         return btn;
48371     },
48372
48373      /**
48374      * Adds a series of form elements (using the xtype property as the factory method.
48375      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48376      * @param {Object} config 
48377      */
48378     
48379     addxtype : function()
48380     {
48381         var ar = Array.prototype.slice.call(arguments, 0);
48382         var ret = false;
48383         for(var i = 0; i < ar.length; i++) {
48384             if (!ar[i]) {
48385                 continue; // skip -- if this happends something invalid got sent, we 
48386                 // should ignore it, as basically that interface element will not show up
48387                 // and that should be pretty obvious!!
48388             }
48389             
48390             if (Roo.form[ar[i].xtype]) {
48391                 ar[i].form = this;
48392                 var fe = Roo.factory(ar[i], Roo.form);
48393                 if (!ret) {
48394                     ret = fe;
48395                 }
48396                 fe.form = this;
48397                 if (fe.store) {
48398                     fe.store.form = this;
48399                 }
48400                 if (fe.isLayout) {  
48401                          
48402                     this.start(fe);
48403                     this.allItems.push(fe);
48404                     if (fe.items && fe.addxtype) {
48405                         fe.addxtype.apply(fe, fe.items);
48406                         delete fe.items;
48407                     }
48408                      this.end();
48409                     continue;
48410                 }
48411                 
48412                 
48413                  
48414                 this.add(fe);
48415               //  console.log('adding ' + ar[i].xtype);
48416             }
48417             if (ar[i].xtype == 'Button') {  
48418                 //console.log('adding button');
48419                 //console.log(ar[i]);
48420                 this.addButton(ar[i]);
48421                 this.allItems.push(fe);
48422                 continue;
48423             }
48424             
48425             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48426                 alert('end is not supported on xtype any more, use items');
48427             //    this.end();
48428             //    //console.log('adding end');
48429             }
48430             
48431         }
48432         return ret;
48433     },
48434     
48435     /**
48436      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48437      * option "monitorValid"
48438      */
48439     startMonitoring : function(){
48440         if(!this.bound){
48441             this.bound = true;
48442             Roo.TaskMgr.start({
48443                 run : this.bindHandler,
48444                 interval : this.monitorPoll || 200,
48445                 scope: this
48446             });
48447         }
48448     },
48449
48450     /**
48451      * Stops monitoring of the valid state of this form
48452      */
48453     stopMonitoring : function(){
48454         this.bound = false;
48455     },
48456
48457     // private
48458     bindHandler : function(){
48459         if(!this.bound){
48460             return false; // stops binding
48461         }
48462         var valid = true;
48463         this.items.each(function(f){
48464             if(!f.isValid(true)){
48465                 valid = false;
48466                 return false;
48467             }
48468         });
48469         for(var i = 0, len = this.buttons.length; i < len; i++){
48470             var btn = this.buttons[i];
48471             if(btn.formBind === true && btn.disabled === valid){
48472                 btn.setDisabled(!valid);
48473             }
48474         }
48475         this.fireEvent('clientvalidation', this, valid);
48476     }
48477     
48478     
48479     
48480     
48481     
48482     
48483     
48484     
48485 });
48486
48487
48488 // back compat
48489 Roo.Form = Roo.form.Form;
48490 /*
48491  * Based on:
48492  * Ext JS Library 1.1.1
48493  * Copyright(c) 2006-2007, Ext JS, LLC.
48494  *
48495  * Originally Released Under LGPL - original licence link has changed is not relivant.
48496  *
48497  * Fork - LGPL
48498  * <script type="text/javascript">
48499  */
48500
48501 // as we use this in bootstrap.
48502 Roo.namespace('Roo.form');
48503  /**
48504  * @class Roo.form.Action
48505  * Internal Class used to handle form actions
48506  * @constructor
48507  * @param {Roo.form.BasicForm} el The form element or its id
48508  * @param {Object} config Configuration options
48509  */
48510
48511  
48512  
48513 // define the action interface
48514 Roo.form.Action = function(form, options){
48515     this.form = form;
48516     this.options = options || {};
48517 };
48518 /**
48519  * Client Validation Failed
48520  * @const 
48521  */
48522 Roo.form.Action.CLIENT_INVALID = 'client';
48523 /**
48524  * Server Validation Failed
48525  * @const 
48526  */
48527 Roo.form.Action.SERVER_INVALID = 'server';
48528  /**
48529  * Connect to Server Failed
48530  * @const 
48531  */
48532 Roo.form.Action.CONNECT_FAILURE = 'connect';
48533 /**
48534  * Reading Data from Server Failed
48535  * @const 
48536  */
48537 Roo.form.Action.LOAD_FAILURE = 'load';
48538
48539 Roo.form.Action.prototype = {
48540     type : 'default',
48541     failureType : undefined,
48542     response : undefined,
48543     result : undefined,
48544
48545     // interface method
48546     run : function(options){
48547
48548     },
48549
48550     // interface method
48551     success : function(response){
48552
48553     },
48554
48555     // interface method
48556     handleResponse : function(response){
48557
48558     },
48559
48560     // default connection failure
48561     failure : function(response){
48562         
48563         this.response = response;
48564         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48565         this.form.afterAction(this, false);
48566     },
48567
48568     processResponse : function(response){
48569         this.response = response;
48570         if(!response.responseText){
48571             return true;
48572         }
48573         this.result = this.handleResponse(response);
48574         return this.result;
48575     },
48576
48577     // utility functions used internally
48578     getUrl : function(appendParams){
48579         var url = this.options.url || this.form.url || this.form.el.dom.action;
48580         if(appendParams){
48581             var p = this.getParams();
48582             if(p){
48583                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48584             }
48585         }
48586         return url;
48587     },
48588
48589     getMethod : function(){
48590         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48591     },
48592
48593     getParams : function(){
48594         var bp = this.form.baseParams;
48595         var p = this.options.params;
48596         if(p){
48597             if(typeof p == "object"){
48598                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48599             }else if(typeof p == 'string' && bp){
48600                 p += '&' + Roo.urlEncode(bp);
48601             }
48602         }else if(bp){
48603             p = Roo.urlEncode(bp);
48604         }
48605         return p;
48606     },
48607
48608     createCallback : function(){
48609         return {
48610             success: this.success,
48611             failure: this.failure,
48612             scope: this,
48613             timeout: (this.form.timeout*1000),
48614             upload: this.form.fileUpload ? this.success : undefined
48615         };
48616     }
48617 };
48618
48619 Roo.form.Action.Submit = function(form, options){
48620     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48621 };
48622
48623 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48624     type : 'submit',
48625
48626     haveProgress : false,
48627     uploadComplete : false,
48628     
48629     // uploadProgress indicator.
48630     uploadProgress : function()
48631     {
48632         if (!this.form.progressUrl) {
48633             return;
48634         }
48635         
48636         if (!this.haveProgress) {
48637             Roo.MessageBox.progress("Uploading", "Uploading");
48638         }
48639         if (this.uploadComplete) {
48640            Roo.MessageBox.hide();
48641            return;
48642         }
48643         
48644         this.haveProgress = true;
48645    
48646         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
48647         
48648         var c = new Roo.data.Connection();
48649         c.request({
48650             url : this.form.progressUrl,
48651             params: {
48652                 id : uid
48653             },
48654             method: 'GET',
48655             success : function(req){
48656                //console.log(data);
48657                 var rdata = false;
48658                 var edata;
48659                 try  {
48660                    rdata = Roo.decode(req.responseText)
48661                 } catch (e) {
48662                     Roo.log("Invalid data from server..");
48663                     Roo.log(edata);
48664                     return;
48665                 }
48666                 if (!rdata || !rdata.success) {
48667                     Roo.log(rdata);
48668                     Roo.MessageBox.alert(Roo.encode(rdata));
48669                     return;
48670                 }
48671                 var data = rdata.data;
48672                 
48673                 if (this.uploadComplete) {
48674                    Roo.MessageBox.hide();
48675                    return;
48676                 }
48677                    
48678                 if (data){
48679                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
48680                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
48681                     );
48682                 }
48683                 this.uploadProgress.defer(2000,this);
48684             },
48685        
48686             failure: function(data) {
48687                 Roo.log('progress url failed ');
48688                 Roo.log(data);
48689             },
48690             scope : this
48691         });
48692            
48693     },
48694     
48695     
48696     run : function()
48697     {
48698         // run get Values on the form, so it syncs any secondary forms.
48699         this.form.getValues();
48700         
48701         var o = this.options;
48702         var method = this.getMethod();
48703         var isPost = method == 'POST';
48704         if(o.clientValidation === false || this.form.isValid()){
48705             
48706             if (this.form.progressUrl) {
48707                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
48708                     (new Date() * 1) + '' + Math.random());
48709                     
48710             } 
48711             
48712             
48713             Roo.Ajax.request(Roo.apply(this.createCallback(), {
48714                 form:this.form.el.dom,
48715                 url:this.getUrl(!isPost),
48716                 method: method,
48717                 params:isPost ? this.getParams() : null,
48718                 isUpload: this.form.fileUpload,
48719                 formData : this.form.formData
48720             }));
48721             
48722             this.uploadProgress();
48723
48724         }else if (o.clientValidation !== false){ // client validation failed
48725             this.failureType = Roo.form.Action.CLIENT_INVALID;
48726             this.form.afterAction(this, false);
48727         }
48728     },
48729
48730     success : function(response)
48731     {
48732         this.uploadComplete= true;
48733         if (this.haveProgress) {
48734             Roo.MessageBox.hide();
48735         }
48736         
48737         
48738         var result = this.processResponse(response);
48739         if(result === true || result.success){
48740             this.form.afterAction(this, true);
48741             return;
48742         }
48743         if(result.errors){
48744             this.form.markInvalid(result.errors);
48745             this.failureType = Roo.form.Action.SERVER_INVALID;
48746         }
48747         this.form.afterAction(this, false);
48748     },
48749     failure : function(response)
48750     {
48751         this.uploadComplete= true;
48752         if (this.haveProgress) {
48753             Roo.MessageBox.hide();
48754         }
48755         
48756         this.response = response;
48757         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48758         this.form.afterAction(this, false);
48759     },
48760     
48761     handleResponse : function(response){
48762         if(this.form.errorReader){
48763             var rs = this.form.errorReader.read(response);
48764             var errors = [];
48765             if(rs.records){
48766                 for(var i = 0, len = rs.records.length; i < len; i++) {
48767                     var r = rs.records[i];
48768                     errors[i] = r.data;
48769                 }
48770             }
48771             if(errors.length < 1){
48772                 errors = null;
48773             }
48774             return {
48775                 success : rs.success,
48776                 errors : errors
48777             };
48778         }
48779         var ret = false;
48780         try {
48781             ret = Roo.decode(response.responseText);
48782         } catch (e) {
48783             ret = {
48784                 success: false,
48785                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
48786                 errors : []
48787             };
48788         }
48789         return ret;
48790         
48791     }
48792 });
48793
48794
48795 Roo.form.Action.Load = function(form, options){
48796     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
48797     this.reader = this.form.reader;
48798 };
48799
48800 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
48801     type : 'load',
48802
48803     run : function(){
48804         
48805         Roo.Ajax.request(Roo.apply(
48806                 this.createCallback(), {
48807                     method:this.getMethod(),
48808                     url:this.getUrl(false),
48809                     params:this.getParams()
48810         }));
48811     },
48812
48813     success : function(response){
48814         
48815         var result = this.processResponse(response);
48816         if(result === true || !result.success || !result.data){
48817             this.failureType = Roo.form.Action.LOAD_FAILURE;
48818             this.form.afterAction(this, false);
48819             return;
48820         }
48821         this.form.clearInvalid();
48822         this.form.setValues(result.data);
48823         this.form.afterAction(this, true);
48824     },
48825
48826     handleResponse : function(response){
48827         if(this.form.reader){
48828             var rs = this.form.reader.read(response);
48829             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
48830             return {
48831                 success : rs.success,
48832                 data : data
48833             };
48834         }
48835         return Roo.decode(response.responseText);
48836     }
48837 });
48838
48839 Roo.form.Action.ACTION_TYPES = {
48840     'load' : Roo.form.Action.Load,
48841     'submit' : Roo.form.Action.Submit
48842 };/*
48843  * Based on:
48844  * Ext JS Library 1.1.1
48845  * Copyright(c) 2006-2007, Ext JS, LLC.
48846  *
48847  * Originally Released Under LGPL - original licence link has changed is not relivant.
48848  *
48849  * Fork - LGPL
48850  * <script type="text/javascript">
48851  */
48852  
48853 /**
48854  * @class Roo.form.Layout
48855  * @extends Roo.Component
48856  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
48857  * @constructor
48858  * @param {Object} config Configuration options
48859  */
48860 Roo.form.Layout = function(config){
48861     var xitems = [];
48862     if (config.items) {
48863         xitems = config.items;
48864         delete config.items;
48865     }
48866     Roo.form.Layout.superclass.constructor.call(this, config);
48867     this.stack = [];
48868     Roo.each(xitems, this.addxtype, this);
48869      
48870 };
48871
48872 Roo.extend(Roo.form.Layout, Roo.Component, {
48873     /**
48874      * @cfg {String/Object} autoCreate
48875      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
48876      */
48877     /**
48878      * @cfg {String/Object/Function} style
48879      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
48880      * a function which returns such a specification.
48881      */
48882     /**
48883      * @cfg {String} labelAlign
48884      * Valid values are "left," "top" and "right" (defaults to "left")
48885      */
48886     /**
48887      * @cfg {Number} labelWidth
48888      * Fixed width in pixels of all field labels (defaults to undefined)
48889      */
48890     /**
48891      * @cfg {Boolean} clear
48892      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
48893      */
48894     clear : true,
48895     /**
48896      * @cfg {String} labelSeparator
48897      * The separator to use after field labels (defaults to ':')
48898      */
48899     labelSeparator : ':',
48900     /**
48901      * @cfg {Boolean} hideLabels
48902      * True to suppress the display of field labels in this layout (defaults to false)
48903      */
48904     hideLabels : false,
48905
48906     // private
48907     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
48908     
48909     isLayout : true,
48910     
48911     // private
48912     onRender : function(ct, position){
48913         if(this.el){ // from markup
48914             this.el = Roo.get(this.el);
48915         }else {  // generate
48916             var cfg = this.getAutoCreate();
48917             this.el = ct.createChild(cfg, position);
48918         }
48919         if(this.style){
48920             this.el.applyStyles(this.style);
48921         }
48922         if(this.labelAlign){
48923             this.el.addClass('x-form-label-'+this.labelAlign);
48924         }
48925         if(this.hideLabels){
48926             this.labelStyle = "display:none";
48927             this.elementStyle = "padding-left:0;";
48928         }else{
48929             if(typeof this.labelWidth == 'number'){
48930                 this.labelStyle = "width:"+this.labelWidth+"px;";
48931                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
48932             }
48933             if(this.labelAlign == 'top'){
48934                 this.labelStyle = "width:auto;";
48935                 this.elementStyle = "padding-left:0;";
48936             }
48937         }
48938         var stack = this.stack;
48939         var slen = stack.length;
48940         if(slen > 0){
48941             if(!this.fieldTpl){
48942                 var t = new Roo.Template(
48943                     '<div class="x-form-item {5}">',
48944                         '<label for="{0}" style="{2}">{1}{4}</label>',
48945                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
48946                         '</div>',
48947                     '</div><div class="x-form-clear-left"></div>'
48948                 );
48949                 t.disableFormats = true;
48950                 t.compile();
48951                 Roo.form.Layout.prototype.fieldTpl = t;
48952             }
48953             for(var i = 0; i < slen; i++) {
48954                 if(stack[i].isFormField){
48955                     this.renderField(stack[i]);
48956                 }else{
48957                     this.renderComponent(stack[i]);
48958                 }
48959             }
48960         }
48961         if(this.clear){
48962             this.el.createChild({cls:'x-form-clear'});
48963         }
48964     },
48965
48966     // private
48967     renderField : function(f){
48968         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
48969                f.id, //0
48970                f.fieldLabel, //1
48971                f.labelStyle||this.labelStyle||'', //2
48972                this.elementStyle||'', //3
48973                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
48974                f.itemCls||this.itemCls||''  //5
48975        ], true).getPrevSibling());
48976     },
48977
48978     // private
48979     renderComponent : function(c){
48980         c.render(c.isLayout ? this.el : this.el.createChild());    
48981     },
48982     /**
48983      * Adds a object form elements (using the xtype property as the factory method.)
48984      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
48985      * @param {Object} config 
48986      */
48987     addxtype : function(o)
48988     {
48989         // create the lement.
48990         o.form = this.form;
48991         var fe = Roo.factory(o, Roo.form);
48992         this.form.allItems.push(fe);
48993         this.stack.push(fe);
48994         
48995         if (fe.isFormField) {
48996             this.form.items.add(fe);
48997         }
48998          
48999         return fe;
49000     }
49001 });
49002
49003 /**
49004  * @class Roo.form.Column
49005  * @extends Roo.form.Layout
49006  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49007  * @constructor
49008  * @param {Object} config Configuration options
49009  */
49010 Roo.form.Column = function(config){
49011     Roo.form.Column.superclass.constructor.call(this, config);
49012 };
49013
49014 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49015     /**
49016      * @cfg {Number/String} width
49017      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49018      */
49019     /**
49020      * @cfg {String/Object} autoCreate
49021      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49022      */
49023
49024     // private
49025     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49026
49027     // private
49028     onRender : function(ct, position){
49029         Roo.form.Column.superclass.onRender.call(this, ct, position);
49030         if(this.width){
49031             this.el.setWidth(this.width);
49032         }
49033     }
49034 });
49035
49036
49037 /**
49038  * @class Roo.form.Row
49039  * @extends Roo.form.Layout
49040  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49041  * @constructor
49042  * @param {Object} config Configuration options
49043  */
49044
49045  
49046 Roo.form.Row = function(config){
49047     Roo.form.Row.superclass.constructor.call(this, config);
49048 };
49049  
49050 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49051       /**
49052      * @cfg {Number/String} width
49053      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49054      */
49055     /**
49056      * @cfg {Number/String} height
49057      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49058      */
49059     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49060     
49061     padWidth : 20,
49062     // private
49063     onRender : function(ct, position){
49064         //console.log('row render');
49065         if(!this.rowTpl){
49066             var t = new Roo.Template(
49067                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49068                     '<label for="{0}" style="{2}">{1}{4}</label>',
49069                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49070                     '</div>',
49071                 '</div>'
49072             );
49073             t.disableFormats = true;
49074             t.compile();
49075             Roo.form.Layout.prototype.rowTpl = t;
49076         }
49077         this.fieldTpl = this.rowTpl;
49078         
49079         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49080         var labelWidth = 100;
49081         
49082         if ((this.labelAlign != 'top')) {
49083             if (typeof this.labelWidth == 'number') {
49084                 labelWidth = this.labelWidth
49085             }
49086             this.padWidth =  20 + labelWidth;
49087             
49088         }
49089         
49090         Roo.form.Column.superclass.onRender.call(this, ct, position);
49091         if(this.width){
49092             this.el.setWidth(this.width);
49093         }
49094         if(this.height){
49095             this.el.setHeight(this.height);
49096         }
49097     },
49098     
49099     // private
49100     renderField : function(f){
49101         f.fieldEl = this.fieldTpl.append(this.el, [
49102                f.id, f.fieldLabel,
49103                f.labelStyle||this.labelStyle||'',
49104                this.elementStyle||'',
49105                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49106                f.itemCls||this.itemCls||'',
49107                f.width ? f.width + this.padWidth : 160 + this.padWidth
49108        ],true);
49109     }
49110 });
49111  
49112
49113 /**
49114  * @class Roo.form.FieldSet
49115  * @extends Roo.form.Layout
49116  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49117  * @constructor
49118  * @param {Object} config Configuration options
49119  */
49120 Roo.form.FieldSet = function(config){
49121     Roo.form.FieldSet.superclass.constructor.call(this, config);
49122 };
49123
49124 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49125     /**
49126      * @cfg {String} legend
49127      * The text to display as the legend for the FieldSet (defaults to '')
49128      */
49129     /**
49130      * @cfg {String/Object} autoCreate
49131      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49132      */
49133
49134     // private
49135     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49136
49137     // private
49138     onRender : function(ct, position){
49139         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49140         if(this.legend){
49141             this.setLegend(this.legend);
49142         }
49143     },
49144
49145     // private
49146     setLegend : function(text){
49147         if(this.rendered){
49148             this.el.child('legend').update(text);
49149         }
49150     }
49151 });/*
49152  * Based on:
49153  * Ext JS Library 1.1.1
49154  * Copyright(c) 2006-2007, Ext JS, LLC.
49155  *
49156  * Originally Released Under LGPL - original licence link has changed is not relivant.
49157  *
49158  * Fork - LGPL
49159  * <script type="text/javascript">
49160  */
49161 /**
49162  * @class Roo.form.VTypes
49163  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49164  * @singleton
49165  */
49166 Roo.form.VTypes = function(){
49167     // closure these in so they are only created once.
49168     var alpha = /^[a-zA-Z_]+$/;
49169     var alphanum = /^[a-zA-Z0-9_]+$/;
49170     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49171     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49172
49173     // All these messages and functions are configurable
49174     return {
49175         /**
49176          * The function used to validate email addresses
49177          * @param {String} value The email address
49178          */
49179         'email' : function(v){
49180             return email.test(v);
49181         },
49182         /**
49183          * The error text to display when the email validation function returns false
49184          * @type String
49185          */
49186         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49187         /**
49188          * The keystroke filter mask to be applied on email input
49189          * @type RegExp
49190          */
49191         'emailMask' : /[a-z0-9_\.\-@]/i,
49192
49193         /**
49194          * The function used to validate URLs
49195          * @param {String} value The URL
49196          */
49197         'url' : function(v){
49198             return url.test(v);
49199         },
49200         /**
49201          * The error text to display when the url validation function returns false
49202          * @type String
49203          */
49204         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49205         
49206         /**
49207          * The function used to validate alpha values
49208          * @param {String} value The value
49209          */
49210         'alpha' : function(v){
49211             return alpha.test(v);
49212         },
49213         /**
49214          * The error text to display when the alpha validation function returns false
49215          * @type String
49216          */
49217         'alphaText' : 'This field should only contain letters and _',
49218         /**
49219          * The keystroke filter mask to be applied on alpha input
49220          * @type RegExp
49221          */
49222         'alphaMask' : /[a-z_]/i,
49223
49224         /**
49225          * The function used to validate alphanumeric values
49226          * @param {String} value The value
49227          */
49228         'alphanum' : function(v){
49229             return alphanum.test(v);
49230         },
49231         /**
49232          * The error text to display when the alphanumeric validation function returns false
49233          * @type String
49234          */
49235         'alphanumText' : 'This field should only contain letters, numbers and _',
49236         /**
49237          * The keystroke filter mask to be applied on alphanumeric input
49238          * @type RegExp
49239          */
49240         'alphanumMask' : /[a-z0-9_]/i
49241     };
49242 }();//<script type="text/javascript">
49243
49244 /**
49245  * @class Roo.form.FCKeditor
49246  * @extends Roo.form.TextArea
49247  * Wrapper around the FCKEditor http://www.fckeditor.net
49248  * @constructor
49249  * Creates a new FCKeditor
49250  * @param {Object} config Configuration options
49251  */
49252 Roo.form.FCKeditor = function(config){
49253     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49254     this.addEvents({
49255          /**
49256          * @event editorinit
49257          * Fired when the editor is initialized - you can add extra handlers here..
49258          * @param {FCKeditor} this
49259          * @param {Object} the FCK object.
49260          */
49261         editorinit : true
49262     });
49263     
49264     
49265 };
49266 Roo.form.FCKeditor.editors = { };
49267 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49268 {
49269     //defaultAutoCreate : {
49270     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49271     //},
49272     // private
49273     /**
49274      * @cfg {Object} fck options - see fck manual for details.
49275      */
49276     fckconfig : false,
49277     
49278     /**
49279      * @cfg {Object} fck toolbar set (Basic or Default)
49280      */
49281     toolbarSet : 'Basic',
49282     /**
49283      * @cfg {Object} fck BasePath
49284      */ 
49285     basePath : '/fckeditor/',
49286     
49287     
49288     frame : false,
49289     
49290     value : '',
49291     
49292    
49293     onRender : function(ct, position)
49294     {
49295         if(!this.el){
49296             this.defaultAutoCreate = {
49297                 tag: "textarea",
49298                 style:"width:300px;height:60px;",
49299                 autocomplete: "new-password"
49300             };
49301         }
49302         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49303         /*
49304         if(this.grow){
49305             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49306             if(this.preventScrollbars){
49307                 this.el.setStyle("overflow", "hidden");
49308             }
49309             this.el.setHeight(this.growMin);
49310         }
49311         */
49312         //console.log('onrender' + this.getId() );
49313         Roo.form.FCKeditor.editors[this.getId()] = this;
49314          
49315
49316         this.replaceTextarea() ;
49317         
49318     },
49319     
49320     getEditor : function() {
49321         return this.fckEditor;
49322     },
49323     /**
49324      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49325      * @param {Mixed} value The value to set
49326      */
49327     
49328     
49329     setValue : function(value)
49330     {
49331         //console.log('setValue: ' + value);
49332         
49333         if(typeof(value) == 'undefined') { // not sure why this is happending...
49334             return;
49335         }
49336         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49337         
49338         //if(!this.el || !this.getEditor()) {
49339         //    this.value = value;
49340             //this.setValue.defer(100,this,[value]);    
49341         //    return;
49342         //} 
49343         
49344         if(!this.getEditor()) {
49345             return;
49346         }
49347         
49348         this.getEditor().SetData(value);
49349         
49350         //
49351
49352     },
49353
49354     /**
49355      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49356      * @return {Mixed} value The field value
49357      */
49358     getValue : function()
49359     {
49360         
49361         if (this.frame && this.frame.dom.style.display == 'none') {
49362             return Roo.form.FCKeditor.superclass.getValue.call(this);
49363         }
49364         
49365         if(!this.el || !this.getEditor()) {
49366            
49367            // this.getValue.defer(100,this); 
49368             return this.value;
49369         }
49370        
49371         
49372         var value=this.getEditor().GetData();
49373         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49374         return Roo.form.FCKeditor.superclass.getValue.call(this);
49375         
49376
49377     },
49378
49379     /**
49380      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49381      * @return {Mixed} value The field value
49382      */
49383     getRawValue : function()
49384     {
49385         if (this.frame && this.frame.dom.style.display == 'none') {
49386             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49387         }
49388         
49389         if(!this.el || !this.getEditor()) {
49390             //this.getRawValue.defer(100,this); 
49391             return this.value;
49392             return;
49393         }
49394         
49395         
49396         
49397         var value=this.getEditor().GetData();
49398         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49399         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49400          
49401     },
49402     
49403     setSize : function(w,h) {
49404         
49405         
49406         
49407         //if (this.frame && this.frame.dom.style.display == 'none') {
49408         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49409         //    return;
49410         //}
49411         //if(!this.el || !this.getEditor()) {
49412         //    this.setSize.defer(100,this, [w,h]); 
49413         //    return;
49414         //}
49415         
49416         
49417         
49418         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49419         
49420         this.frame.dom.setAttribute('width', w);
49421         this.frame.dom.setAttribute('height', h);
49422         this.frame.setSize(w,h);
49423         
49424     },
49425     
49426     toggleSourceEdit : function(value) {
49427         
49428       
49429          
49430         this.el.dom.style.display = value ? '' : 'none';
49431         this.frame.dom.style.display = value ?  'none' : '';
49432         
49433     },
49434     
49435     
49436     focus: function(tag)
49437     {
49438         if (this.frame.dom.style.display == 'none') {
49439             return Roo.form.FCKeditor.superclass.focus.call(this);
49440         }
49441         if(!this.el || !this.getEditor()) {
49442             this.focus.defer(100,this, [tag]); 
49443             return;
49444         }
49445         
49446         
49447         
49448         
49449         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49450         this.getEditor().Focus();
49451         if (tgs.length) {
49452             if (!this.getEditor().Selection.GetSelection()) {
49453                 this.focus.defer(100,this, [tag]); 
49454                 return;
49455             }
49456             
49457             
49458             var r = this.getEditor().EditorDocument.createRange();
49459             r.setStart(tgs[0],0);
49460             r.setEnd(tgs[0],0);
49461             this.getEditor().Selection.GetSelection().removeAllRanges();
49462             this.getEditor().Selection.GetSelection().addRange(r);
49463             this.getEditor().Focus();
49464         }
49465         
49466     },
49467     
49468     
49469     
49470     replaceTextarea : function()
49471     {
49472         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49473             return ;
49474         }
49475         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49476         //{
49477             // We must check the elements firstly using the Id and then the name.
49478         var oTextarea = document.getElementById( this.getId() );
49479         
49480         var colElementsByName = document.getElementsByName( this.getId() ) ;
49481          
49482         oTextarea.style.display = 'none' ;
49483
49484         if ( oTextarea.tabIndex ) {            
49485             this.TabIndex = oTextarea.tabIndex ;
49486         }
49487         
49488         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49489         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49490         this.frame = Roo.get(this.getId() + '___Frame')
49491     },
49492     
49493     _getConfigHtml : function()
49494     {
49495         var sConfig = '' ;
49496
49497         for ( var o in this.fckconfig ) {
49498             sConfig += sConfig.length > 0  ? '&amp;' : '';
49499             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49500         }
49501
49502         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49503     },
49504     
49505     
49506     _getIFrameHtml : function()
49507     {
49508         var sFile = 'fckeditor.html' ;
49509         /* no idea what this is about..
49510         try
49511         {
49512             if ( (/fcksource=true/i).test( window.top.location.search ) )
49513                 sFile = 'fckeditor.original.html' ;
49514         }
49515         catch (e) { 
49516         */
49517
49518         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49519         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49520         
49521         
49522         var html = '<iframe id="' + this.getId() +
49523             '___Frame" src="' + sLink +
49524             '" width="' + this.width +
49525             '" height="' + this.height + '"' +
49526             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49527             ' frameborder="0" scrolling="no"></iframe>' ;
49528
49529         return html ;
49530     },
49531     
49532     _insertHtmlBefore : function( html, element )
49533     {
49534         if ( element.insertAdjacentHTML )       {
49535             // IE
49536             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49537         } else { // Gecko
49538             var oRange = document.createRange() ;
49539             oRange.setStartBefore( element ) ;
49540             var oFragment = oRange.createContextualFragment( html );
49541             element.parentNode.insertBefore( oFragment, element ) ;
49542         }
49543     }
49544     
49545     
49546   
49547     
49548     
49549     
49550     
49551
49552 });
49553
49554 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49555
49556 function FCKeditor_OnComplete(editorInstance){
49557     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49558     f.fckEditor = editorInstance;
49559     //console.log("loaded");
49560     f.fireEvent('editorinit', f, editorInstance);
49561
49562   
49563
49564  
49565
49566
49567
49568
49569
49570
49571
49572
49573
49574
49575
49576
49577
49578
49579
49580 //<script type="text/javascript">
49581 /**
49582  * @class Roo.form.GridField
49583  * @extends Roo.form.Field
49584  * Embed a grid (or editable grid into a form)
49585  * STATUS ALPHA
49586  * 
49587  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49588  * it needs 
49589  * xgrid.store = Roo.data.Store
49590  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49591  * xgrid.store.reader = Roo.data.JsonReader 
49592  * 
49593  * 
49594  * @constructor
49595  * Creates a new GridField
49596  * @param {Object} config Configuration options
49597  */
49598 Roo.form.GridField = function(config){
49599     Roo.form.GridField.superclass.constructor.call(this, config);
49600      
49601 };
49602
49603 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49604     /**
49605      * @cfg {Number} width  - used to restrict width of grid..
49606      */
49607     width : 100,
49608     /**
49609      * @cfg {Number} height - used to restrict height of grid..
49610      */
49611     height : 50,
49612      /**
49613      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49614          * 
49615          *}
49616      */
49617     xgrid : false, 
49618     /**
49619      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49620      * {tag: "input", type: "checkbox", autocomplete: "off"})
49621      */
49622    // defaultAutoCreate : { tag: 'div' },
49623     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49624     /**
49625      * @cfg {String} addTitle Text to include for adding a title.
49626      */
49627     addTitle : false,
49628     //
49629     onResize : function(){
49630         Roo.form.Field.superclass.onResize.apply(this, arguments);
49631     },
49632
49633     initEvents : function(){
49634         // Roo.form.Checkbox.superclass.initEvents.call(this);
49635         // has no events...
49636        
49637     },
49638
49639
49640     getResizeEl : function(){
49641         return this.wrap;
49642     },
49643
49644     getPositionEl : function(){
49645         return this.wrap;
49646     },
49647
49648     // private
49649     onRender : function(ct, position){
49650         
49651         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
49652         var style = this.style;
49653         delete this.style;
49654         
49655         Roo.form.GridField.superclass.onRender.call(this, ct, position);
49656         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
49657         this.viewEl = this.wrap.createChild({ tag: 'div' });
49658         if (style) {
49659             this.viewEl.applyStyles(style);
49660         }
49661         if (this.width) {
49662             this.viewEl.setWidth(this.width);
49663         }
49664         if (this.height) {
49665             this.viewEl.setHeight(this.height);
49666         }
49667         //if(this.inputValue !== undefined){
49668         //this.setValue(this.value);
49669         
49670         
49671         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
49672         
49673         
49674         this.grid.render();
49675         this.grid.getDataSource().on('remove', this.refreshValue, this);
49676         this.grid.getDataSource().on('update', this.refreshValue, this);
49677         this.grid.on('afteredit', this.refreshValue, this);
49678  
49679     },
49680      
49681     
49682     /**
49683      * Sets the value of the item. 
49684      * @param {String} either an object  or a string..
49685      */
49686     setValue : function(v){
49687         //this.value = v;
49688         v = v || []; // empty set..
49689         // this does not seem smart - it really only affects memoryproxy grids..
49690         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
49691             var ds = this.grid.getDataSource();
49692             // assumes a json reader..
49693             var data = {}
49694             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
49695             ds.loadData( data);
49696         }
49697         // clear selection so it does not get stale.
49698         if (this.grid.sm) { 
49699             this.grid.sm.clearSelections();
49700         }
49701         
49702         Roo.form.GridField.superclass.setValue.call(this, v);
49703         this.refreshValue();
49704         // should load data in the grid really....
49705     },
49706     
49707     // private
49708     refreshValue: function() {
49709          var val = [];
49710         this.grid.getDataSource().each(function(r) {
49711             val.push(r.data);
49712         });
49713         this.el.dom.value = Roo.encode(val);
49714     }
49715     
49716      
49717     
49718     
49719 });/*
49720  * Based on:
49721  * Ext JS Library 1.1.1
49722  * Copyright(c) 2006-2007, Ext JS, LLC.
49723  *
49724  * Originally Released Under LGPL - original licence link has changed is not relivant.
49725  *
49726  * Fork - LGPL
49727  * <script type="text/javascript">
49728  */
49729 /**
49730  * @class Roo.form.DisplayField
49731  * @extends Roo.form.Field
49732  * A generic Field to display non-editable data.
49733  * @cfg {Boolean} closable (true|false) default false
49734  * @constructor
49735  * Creates a new Display Field item.
49736  * @param {Object} config Configuration options
49737  */
49738 Roo.form.DisplayField = function(config){
49739     Roo.form.DisplayField.superclass.constructor.call(this, config);
49740     
49741     this.addEvents({
49742         /**
49743          * @event close
49744          * Fires after the click the close btn
49745              * @param {Roo.form.DisplayField} this
49746              */
49747         close : true
49748     });
49749 };
49750
49751 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
49752     inputType:      'hidden',
49753     allowBlank:     true,
49754     readOnly:         true,
49755     
49756  
49757     /**
49758      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49759      */
49760     focusClass : undefined,
49761     /**
49762      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49763      */
49764     fieldClass: 'x-form-field',
49765     
49766      /**
49767      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
49768      */
49769     valueRenderer: undefined,
49770     
49771     width: 100,
49772     /**
49773      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49774      * {tag: "input", type: "checkbox", autocomplete: "off"})
49775      */
49776      
49777  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
49778  
49779     closable : false,
49780     
49781     onResize : function(){
49782         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
49783         
49784     },
49785
49786     initEvents : function(){
49787         // Roo.form.Checkbox.superclass.initEvents.call(this);
49788         // has no events...
49789         
49790         if(this.closable){
49791             this.closeEl.on('click', this.onClose, this);
49792         }
49793        
49794     },
49795
49796
49797     getResizeEl : function(){
49798         return this.wrap;
49799     },
49800
49801     getPositionEl : function(){
49802         return this.wrap;
49803     },
49804
49805     // private
49806     onRender : function(ct, position){
49807         
49808         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
49809         //if(this.inputValue !== undefined){
49810         this.wrap = this.el.wrap();
49811         
49812         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
49813         
49814         if(this.closable){
49815             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
49816         }
49817         
49818         if (this.bodyStyle) {
49819             this.viewEl.applyStyles(this.bodyStyle);
49820         }
49821         //this.viewEl.setStyle('padding', '2px');
49822         
49823         this.setValue(this.value);
49824         
49825     },
49826 /*
49827     // private
49828     initValue : Roo.emptyFn,
49829
49830   */
49831
49832         // private
49833     onClick : function(){
49834         
49835     },
49836
49837     /**
49838      * Sets the checked state of the checkbox.
49839      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
49840      */
49841     setValue : function(v){
49842         this.value = v;
49843         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
49844         // this might be called before we have a dom element..
49845         if (!this.viewEl) {
49846             return;
49847         }
49848         this.viewEl.dom.innerHTML = html;
49849         Roo.form.DisplayField.superclass.setValue.call(this, v);
49850
49851     },
49852     
49853     onClose : function(e)
49854     {
49855         e.preventDefault();
49856         
49857         this.fireEvent('close', this);
49858     }
49859 });/*
49860  * 
49861  * Licence- LGPL
49862  * 
49863  */
49864
49865 /**
49866  * @class Roo.form.DayPicker
49867  * @extends Roo.form.Field
49868  * A Day picker show [M] [T] [W] ....
49869  * @constructor
49870  * Creates a new Day Picker
49871  * @param {Object} config Configuration options
49872  */
49873 Roo.form.DayPicker= function(config){
49874     Roo.form.DayPicker.superclass.constructor.call(this, config);
49875      
49876 };
49877
49878 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
49879     /**
49880      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
49881      */
49882     focusClass : undefined,
49883     /**
49884      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
49885      */
49886     fieldClass: "x-form-field",
49887    
49888     /**
49889      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49890      * {tag: "input", type: "checkbox", autocomplete: "off"})
49891      */
49892     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
49893     
49894    
49895     actionMode : 'viewEl', 
49896     //
49897     // private
49898  
49899     inputType : 'hidden',
49900     
49901      
49902     inputElement: false, // real input element?
49903     basedOn: false, // ????
49904     
49905     isFormField: true, // not sure where this is needed!!!!
49906
49907     onResize : function(){
49908         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
49909         if(!this.boxLabel){
49910             this.el.alignTo(this.wrap, 'c-c');
49911         }
49912     },
49913
49914     initEvents : function(){
49915         Roo.form.Checkbox.superclass.initEvents.call(this);
49916         this.el.on("click", this.onClick,  this);
49917         this.el.on("change", this.onClick,  this);
49918     },
49919
49920
49921     getResizeEl : function(){
49922         return this.wrap;
49923     },
49924
49925     getPositionEl : function(){
49926         return this.wrap;
49927     },
49928
49929     
49930     // private
49931     onRender : function(ct, position){
49932         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
49933        
49934         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
49935         
49936         var r1 = '<table><tr>';
49937         var r2 = '<tr class="x-form-daypick-icons">';
49938         for (var i=0; i < 7; i++) {
49939             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
49940             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
49941         }
49942         
49943         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
49944         viewEl.select('img').on('click', this.onClick, this);
49945         this.viewEl = viewEl;   
49946         
49947         
49948         // this will not work on Chrome!!!
49949         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
49950         this.el.on('propertychange', this.setFromHidden,  this);  //ie
49951         
49952         
49953           
49954
49955     },
49956
49957     // private
49958     initValue : Roo.emptyFn,
49959
49960     /**
49961      * Returns the checked state of the checkbox.
49962      * @return {Boolean} True if checked, else false
49963      */
49964     getValue : function(){
49965         return this.el.dom.value;
49966         
49967     },
49968
49969         // private
49970     onClick : function(e){ 
49971         //this.setChecked(!this.checked);
49972         Roo.get(e.target).toggleClass('x-menu-item-checked');
49973         this.refreshValue();
49974         //if(this.el.dom.checked != this.checked){
49975         //    this.setValue(this.el.dom.checked);
49976        // }
49977     },
49978     
49979     // private
49980     refreshValue : function()
49981     {
49982         var val = '';
49983         this.viewEl.select('img',true).each(function(e,i,n)  {
49984             val += e.is(".x-menu-item-checked") ? String(n) : '';
49985         });
49986         this.setValue(val, true);
49987     },
49988
49989     /**
49990      * Sets the checked state of the checkbox.
49991      * On is always based on a string comparison between inputValue and the param.
49992      * @param {Boolean/String} value - the value to set 
49993      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
49994      */
49995     setValue : function(v,suppressEvent){
49996         if (!this.el.dom) {
49997             return;
49998         }
49999         var old = this.el.dom.value ;
50000         this.el.dom.value = v;
50001         if (suppressEvent) {
50002             return ;
50003         }
50004          
50005         // update display..
50006         this.viewEl.select('img',true).each(function(e,i,n)  {
50007             
50008             var on = e.is(".x-menu-item-checked");
50009             var newv = v.indexOf(String(n)) > -1;
50010             if (on != newv) {
50011                 e.toggleClass('x-menu-item-checked');
50012             }
50013             
50014         });
50015         
50016         
50017         this.fireEvent('change', this, v, old);
50018         
50019         
50020     },
50021    
50022     // handle setting of hidden value by some other method!!?!?
50023     setFromHidden: function()
50024     {
50025         if(!this.el){
50026             return;
50027         }
50028         //console.log("SET FROM HIDDEN");
50029         //alert('setFrom hidden');
50030         this.setValue(this.el.dom.value);
50031     },
50032     
50033     onDestroy : function()
50034     {
50035         if(this.viewEl){
50036             Roo.get(this.viewEl).remove();
50037         }
50038          
50039         Roo.form.DayPicker.superclass.onDestroy.call(this);
50040     }
50041
50042 });/*
50043  * RooJS Library 1.1.1
50044  * Copyright(c) 2008-2011  Alan Knowles
50045  *
50046  * License - LGPL
50047  */
50048  
50049
50050 /**
50051  * @class Roo.form.ComboCheck
50052  * @extends Roo.form.ComboBox
50053  * A combobox for multiple select items.
50054  *
50055  * FIXME - could do with a reset button..
50056  * 
50057  * @constructor
50058  * Create a new ComboCheck
50059  * @param {Object} config Configuration options
50060  */
50061 Roo.form.ComboCheck = function(config){
50062     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50063     // should verify some data...
50064     // like
50065     // hiddenName = required..
50066     // displayField = required
50067     // valudField == required
50068     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50069     var _t = this;
50070     Roo.each(req, function(e) {
50071         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50072             throw "Roo.form.ComboCheck : missing value for: " + e;
50073         }
50074     });
50075     
50076     
50077 };
50078
50079 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50080      
50081      
50082     editable : false,
50083      
50084     selectedClass: 'x-menu-item-checked', 
50085     
50086     // private
50087     onRender : function(ct, position){
50088         var _t = this;
50089         
50090         
50091         
50092         if(!this.tpl){
50093             var cls = 'x-combo-list';
50094
50095             
50096             this.tpl =  new Roo.Template({
50097                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50098                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50099                    '<span>{' + this.displayField + '}</span>' +
50100                     '</div>' 
50101                 
50102             });
50103         }
50104  
50105         
50106         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50107         this.view.singleSelect = false;
50108         this.view.multiSelect = true;
50109         this.view.toggleSelect = true;
50110         this.pageTb.add(new Roo.Toolbar.Fill(), {
50111             
50112             text: 'Done',
50113             handler: function()
50114             {
50115                 _t.collapse();
50116             }
50117         });
50118     },
50119     
50120     onViewOver : function(e, t){
50121         // do nothing...
50122         return;
50123         
50124     },
50125     
50126     onViewClick : function(doFocus,index){
50127         return;
50128         
50129     },
50130     select: function () {
50131         //Roo.log("SELECT CALLED");
50132     },
50133      
50134     selectByValue : function(xv, scrollIntoView){
50135         var ar = this.getValueArray();
50136         var sels = [];
50137         
50138         Roo.each(ar, function(v) {
50139             if(v === undefined || v === null){
50140                 return;
50141             }
50142             var r = this.findRecord(this.valueField, v);
50143             if(r){
50144                 sels.push(this.store.indexOf(r))
50145                 
50146             }
50147         },this);
50148         this.view.select(sels);
50149         return false;
50150     },
50151     
50152     
50153     
50154     onSelect : function(record, index){
50155        // Roo.log("onselect Called");
50156        // this is only called by the clear button now..
50157         this.view.clearSelections();
50158         this.setValue('[]');
50159         if (this.value != this.valueBefore) {
50160             this.fireEvent('change', this, this.value, this.valueBefore);
50161             this.valueBefore = this.value;
50162         }
50163     },
50164     getValueArray : function()
50165     {
50166         var ar = [] ;
50167         
50168         try {
50169             //Roo.log(this.value);
50170             if (typeof(this.value) == 'undefined') {
50171                 return [];
50172             }
50173             var ar = Roo.decode(this.value);
50174             return  ar instanceof Array ? ar : []; //?? valid?
50175             
50176         } catch(e) {
50177             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50178             return [];
50179         }
50180          
50181     },
50182     expand : function ()
50183     {
50184         
50185         Roo.form.ComboCheck.superclass.expand.call(this);
50186         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50187         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50188         
50189
50190     },
50191     
50192     collapse : function(){
50193         Roo.form.ComboCheck.superclass.collapse.call(this);
50194         var sl = this.view.getSelectedIndexes();
50195         var st = this.store;
50196         var nv = [];
50197         var tv = [];
50198         var r;
50199         Roo.each(sl, function(i) {
50200             r = st.getAt(i);
50201             nv.push(r.get(this.valueField));
50202         },this);
50203         this.setValue(Roo.encode(nv));
50204         if (this.value != this.valueBefore) {
50205
50206             this.fireEvent('change', this, this.value, this.valueBefore);
50207             this.valueBefore = this.value;
50208         }
50209         
50210     },
50211     
50212     setValue : function(v){
50213         // Roo.log(v);
50214         this.value = v;
50215         
50216         var vals = this.getValueArray();
50217         var tv = [];
50218         Roo.each(vals, function(k) {
50219             var r = this.findRecord(this.valueField, k);
50220             if(r){
50221                 tv.push(r.data[this.displayField]);
50222             }else if(this.valueNotFoundText !== undefined){
50223                 tv.push( this.valueNotFoundText );
50224             }
50225         },this);
50226        // Roo.log(tv);
50227         
50228         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50229         this.hiddenField.value = v;
50230         this.value = v;
50231     }
50232     
50233 });/*
50234  * Based on:
50235  * Ext JS Library 1.1.1
50236  * Copyright(c) 2006-2007, Ext JS, LLC.
50237  *
50238  * Originally Released Under LGPL - original licence link has changed is not relivant.
50239  *
50240  * Fork - LGPL
50241  * <script type="text/javascript">
50242  */
50243  
50244 /**
50245  * @class Roo.form.Signature
50246  * @extends Roo.form.Field
50247  * Signature field.  
50248  * @constructor
50249  * 
50250  * @param {Object} config Configuration options
50251  */
50252
50253 Roo.form.Signature = function(config){
50254     Roo.form.Signature.superclass.constructor.call(this, config);
50255     
50256     this.addEvents({// not in used??
50257          /**
50258          * @event confirm
50259          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50260              * @param {Roo.form.Signature} combo This combo box
50261              */
50262         'confirm' : true,
50263         /**
50264          * @event reset
50265          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50266              * @param {Roo.form.ComboBox} combo This combo box
50267              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50268              */
50269         'reset' : true
50270     });
50271 };
50272
50273 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50274     /**
50275      * @cfg {Object} labels Label to use when rendering a form.
50276      * defaults to 
50277      * labels : { 
50278      *      clear : "Clear",
50279      *      confirm : "Confirm"
50280      *  }
50281      */
50282     labels : { 
50283         clear : "Clear",
50284         confirm : "Confirm"
50285     },
50286     /**
50287      * @cfg {Number} width The signature panel width (defaults to 300)
50288      */
50289     width: 300,
50290     /**
50291      * @cfg {Number} height The signature panel height (defaults to 100)
50292      */
50293     height : 100,
50294     /**
50295      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50296      */
50297     allowBlank : false,
50298     
50299     //private
50300     // {Object} signPanel The signature SVG panel element (defaults to {})
50301     signPanel : {},
50302     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50303     isMouseDown : false,
50304     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50305     isConfirmed : false,
50306     // {String} signatureTmp SVG mapping string (defaults to empty string)
50307     signatureTmp : '',
50308     
50309     
50310     defaultAutoCreate : { // modified by initCompnoent..
50311         tag: "input",
50312         type:"hidden"
50313     },
50314
50315     // private
50316     onRender : function(ct, position){
50317         
50318         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50319         
50320         this.wrap = this.el.wrap({
50321             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50322         });
50323         
50324         this.createToolbar(this);
50325         this.signPanel = this.wrap.createChild({
50326                 tag: 'div',
50327                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50328             }, this.el
50329         );
50330             
50331         this.svgID = Roo.id();
50332         this.svgEl = this.signPanel.createChild({
50333               xmlns : 'http://www.w3.org/2000/svg',
50334               tag : 'svg',
50335               id : this.svgID + "-svg",
50336               width: this.width,
50337               height: this.height,
50338               viewBox: '0 0 '+this.width+' '+this.height,
50339               cn : [
50340                 {
50341                     tag: "rect",
50342                     id: this.svgID + "-svg-r",
50343                     width: this.width,
50344                     height: this.height,
50345                     fill: "#ffa"
50346                 },
50347                 {
50348                     tag: "line",
50349                     id: this.svgID + "-svg-l",
50350                     x1: "0", // start
50351                     y1: (this.height*0.8), // start set the line in 80% of height
50352                     x2: this.width, // end
50353                     y2: (this.height*0.8), // end set the line in 80% of height
50354                     'stroke': "#666",
50355                     'stroke-width': "1",
50356                     'stroke-dasharray': "3",
50357                     'shape-rendering': "crispEdges",
50358                     'pointer-events': "none"
50359                 },
50360                 {
50361                     tag: "path",
50362                     id: this.svgID + "-svg-p",
50363                     'stroke': "navy",
50364                     'stroke-width': "3",
50365                     'fill': "none",
50366                     'pointer-events': 'none'
50367                 }
50368               ]
50369         });
50370         this.createSVG();
50371         this.svgBox = this.svgEl.dom.getScreenCTM();
50372     },
50373     createSVG : function(){ 
50374         var svg = this.signPanel;
50375         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50376         var t = this;
50377
50378         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50379         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50380         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50381         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50382         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50383         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50384         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50385         
50386     },
50387     isTouchEvent : function(e){
50388         return e.type.match(/^touch/);
50389     },
50390     getCoords : function (e) {
50391         var pt    = this.svgEl.dom.createSVGPoint();
50392         pt.x = e.clientX; 
50393         pt.y = e.clientY;
50394         if (this.isTouchEvent(e)) {
50395             pt.x =  e.targetTouches[0].clientX;
50396             pt.y = e.targetTouches[0].clientY;
50397         }
50398         var a = this.svgEl.dom.getScreenCTM();
50399         var b = a.inverse();
50400         var mx = pt.matrixTransform(b);
50401         return mx.x + ',' + mx.y;
50402     },
50403     //mouse event headler 
50404     down : function (e) {
50405         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50406         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50407         
50408         this.isMouseDown = true;
50409         
50410         e.preventDefault();
50411     },
50412     move : function (e) {
50413         if (this.isMouseDown) {
50414             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50415             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50416         }
50417         
50418         e.preventDefault();
50419     },
50420     up : function (e) {
50421         this.isMouseDown = false;
50422         var sp = this.signatureTmp.split(' ');
50423         
50424         if(sp.length > 1){
50425             if(!sp[sp.length-2].match(/^L/)){
50426                 sp.pop();
50427                 sp.pop();
50428                 sp.push("");
50429                 this.signatureTmp = sp.join(" ");
50430             }
50431         }
50432         if(this.getValue() != this.signatureTmp){
50433             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50434             this.isConfirmed = false;
50435         }
50436         e.preventDefault();
50437     },
50438     
50439     /**
50440      * Protected method that will not generally be called directly. It
50441      * is called when the editor creates its toolbar. Override this method if you need to
50442      * add custom toolbar buttons.
50443      * @param {HtmlEditor} editor
50444      */
50445     createToolbar : function(editor){
50446          function btn(id, toggle, handler){
50447             var xid = fid + '-'+ id ;
50448             return {
50449                 id : xid,
50450                 cmd : id,
50451                 cls : 'x-btn-icon x-edit-'+id,
50452                 enableToggle:toggle !== false,
50453                 scope: editor, // was editor...
50454                 handler:handler||editor.relayBtnCmd,
50455                 clickEvent:'mousedown',
50456                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50457                 tabIndex:-1
50458             };
50459         }
50460         
50461         
50462         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50463         this.tb = tb;
50464         this.tb.add(
50465            {
50466                 cls : ' x-signature-btn x-signature-'+id,
50467                 scope: editor, // was editor...
50468                 handler: this.reset,
50469                 clickEvent:'mousedown',
50470                 text: this.labels.clear
50471             },
50472             {
50473                  xtype : 'Fill',
50474                  xns: Roo.Toolbar
50475             }, 
50476             {
50477                 cls : '  x-signature-btn x-signature-'+id,
50478                 scope: editor, // was editor...
50479                 handler: this.confirmHandler,
50480                 clickEvent:'mousedown',
50481                 text: this.labels.confirm
50482             }
50483         );
50484     
50485     },
50486     //public
50487     /**
50488      * when user is clicked confirm then show this image.....
50489      * 
50490      * @return {String} Image Data URI
50491      */
50492     getImageDataURI : function(){
50493         var svg = this.svgEl.dom.parentNode.innerHTML;
50494         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50495         return src; 
50496     },
50497     /**
50498      * 
50499      * @return {Boolean} this.isConfirmed
50500      */
50501     getConfirmed : function(){
50502         return this.isConfirmed;
50503     },
50504     /**
50505      * 
50506      * @return {Number} this.width
50507      */
50508     getWidth : function(){
50509         return this.width;
50510     },
50511     /**
50512      * 
50513      * @return {Number} this.height
50514      */
50515     getHeight : function(){
50516         return this.height;
50517     },
50518     // private
50519     getSignature : function(){
50520         return this.signatureTmp;
50521     },
50522     // private
50523     reset : function(){
50524         this.signatureTmp = '';
50525         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50526         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50527         this.isConfirmed = false;
50528         Roo.form.Signature.superclass.reset.call(this);
50529     },
50530     setSignature : function(s){
50531         this.signatureTmp = s;
50532         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50533         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50534         this.setValue(s);
50535         this.isConfirmed = false;
50536         Roo.form.Signature.superclass.reset.call(this);
50537     }, 
50538     test : function(){
50539 //        Roo.log(this.signPanel.dom.contentWindow.up())
50540     },
50541     //private
50542     setConfirmed : function(){
50543         
50544         
50545         
50546 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50547     },
50548     // private
50549     confirmHandler : function(){
50550         if(!this.getSignature()){
50551             return;
50552         }
50553         
50554         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50555         this.setValue(this.getSignature());
50556         this.isConfirmed = true;
50557         
50558         this.fireEvent('confirm', this);
50559     },
50560     // private
50561     // Subclasses should provide the validation implementation by overriding this
50562     validateValue : function(value){
50563         if(this.allowBlank){
50564             return true;
50565         }
50566         
50567         if(this.isConfirmed){
50568             return true;
50569         }
50570         return false;
50571     }
50572 });/*
50573  * Based on:
50574  * Ext JS Library 1.1.1
50575  * Copyright(c) 2006-2007, Ext JS, LLC.
50576  *
50577  * Originally Released Under LGPL - original licence link has changed is not relivant.
50578  *
50579  * Fork - LGPL
50580  * <script type="text/javascript">
50581  */
50582  
50583
50584 /**
50585  * @class Roo.form.ComboBox
50586  * @extends Roo.form.TriggerField
50587  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50588  * @constructor
50589  * Create a new ComboBox.
50590  * @param {Object} config Configuration options
50591  */
50592 Roo.form.Select = function(config){
50593     Roo.form.Select.superclass.constructor.call(this, config);
50594      
50595 };
50596
50597 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50598     /**
50599      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50600      */
50601     /**
50602      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50603      * rendering into an Roo.Editor, defaults to false)
50604      */
50605     /**
50606      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50607      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50608      */
50609     /**
50610      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50611      */
50612     /**
50613      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50614      * the dropdown list (defaults to undefined, with no header element)
50615      */
50616
50617      /**
50618      * @cfg {String/Roo.Template} tpl The template to use to render the output
50619      */
50620      
50621     // private
50622     defaultAutoCreate : {tag: "select"  },
50623     /**
50624      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50625      */
50626     listWidth: undefined,
50627     /**
50628      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50629      * mode = 'remote' or 'text' if mode = 'local')
50630      */
50631     displayField: undefined,
50632     /**
50633      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50634      * mode = 'remote' or 'value' if mode = 'local'). 
50635      * Note: use of a valueField requires the user make a selection
50636      * in order for a value to be mapped.
50637      */
50638     valueField: undefined,
50639     
50640     
50641     /**
50642      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50643      * field's data value (defaults to the underlying DOM element's name)
50644      */
50645     hiddenName: undefined,
50646     /**
50647      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
50648      */
50649     listClass: '',
50650     /**
50651      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
50652      */
50653     selectedClass: 'x-combo-selected',
50654     /**
50655      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
50656      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
50657      * which displays a downward arrow icon).
50658      */
50659     triggerClass : 'x-form-arrow-trigger',
50660     /**
50661      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
50662      */
50663     shadow:'sides',
50664     /**
50665      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
50666      * anchor positions (defaults to 'tl-bl')
50667      */
50668     listAlign: 'tl-bl?',
50669     /**
50670      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
50671      */
50672     maxHeight: 300,
50673     /**
50674      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
50675      * query specified by the allQuery config option (defaults to 'query')
50676      */
50677     triggerAction: 'query',
50678     /**
50679      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
50680      * (defaults to 4, does not apply if editable = false)
50681      */
50682     minChars : 4,
50683     /**
50684      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
50685      * delay (typeAheadDelay) if it matches a known value (defaults to false)
50686      */
50687     typeAhead: false,
50688     /**
50689      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
50690      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
50691      */
50692     queryDelay: 500,
50693     /**
50694      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
50695      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
50696      */
50697     pageSize: 0,
50698     /**
50699      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
50700      * when editable = true (defaults to false)
50701      */
50702     selectOnFocus:false,
50703     /**
50704      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
50705      */
50706     queryParam: 'query',
50707     /**
50708      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
50709      * when mode = 'remote' (defaults to 'Loading...')
50710      */
50711     loadingText: 'Loading...',
50712     /**
50713      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
50714      */
50715     resizable: false,
50716     /**
50717      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
50718      */
50719     handleHeight : 8,
50720     /**
50721      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
50722      * traditional select (defaults to true)
50723      */
50724     editable: true,
50725     /**
50726      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
50727      */
50728     allQuery: '',
50729     /**
50730      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
50731      */
50732     mode: 'remote',
50733     /**
50734      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
50735      * listWidth has a higher value)
50736      */
50737     minListWidth : 70,
50738     /**
50739      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
50740      * allow the user to set arbitrary text into the field (defaults to false)
50741      */
50742     forceSelection:false,
50743     /**
50744      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
50745      * if typeAhead = true (defaults to 250)
50746      */
50747     typeAheadDelay : 250,
50748     /**
50749      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
50750      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
50751      */
50752     valueNotFoundText : undefined,
50753     
50754     /**
50755      * @cfg {String} defaultValue The value displayed after loading the store.
50756      */
50757     defaultValue: '',
50758     
50759     /**
50760      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
50761      */
50762     blockFocus : false,
50763     
50764     /**
50765      * @cfg {Boolean} disableClear Disable showing of clear button.
50766      */
50767     disableClear : false,
50768     /**
50769      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
50770      */
50771     alwaysQuery : false,
50772     
50773     //private
50774     addicon : false,
50775     editicon: false,
50776     
50777     // element that contains real text value.. (when hidden is used..)
50778      
50779     // private
50780     onRender : function(ct, position){
50781         Roo.form.Field.prototype.onRender.call(this, ct, position);
50782         
50783         if(this.store){
50784             this.store.on('beforeload', this.onBeforeLoad, this);
50785             this.store.on('load', this.onLoad, this);
50786             this.store.on('loadexception', this.onLoadException, this);
50787             this.store.load({});
50788         }
50789         
50790         
50791         
50792     },
50793
50794     // private
50795     initEvents : function(){
50796         //Roo.form.ComboBox.superclass.initEvents.call(this);
50797  
50798     },
50799
50800     onDestroy : function(){
50801        
50802         if(this.store){
50803             this.store.un('beforeload', this.onBeforeLoad, this);
50804             this.store.un('load', this.onLoad, this);
50805             this.store.un('loadexception', this.onLoadException, this);
50806         }
50807         //Roo.form.ComboBox.superclass.onDestroy.call(this);
50808     },
50809
50810     // private
50811     fireKey : function(e){
50812         if(e.isNavKeyPress() && !this.list.isVisible()){
50813             this.fireEvent("specialkey", this, e);
50814         }
50815     },
50816
50817     // private
50818     onResize: function(w, h){
50819         
50820         return; 
50821     
50822         
50823     },
50824
50825     /**
50826      * Allow or prevent the user from directly editing the field text.  If false is passed,
50827      * the user will only be able to select from the items defined in the dropdown list.  This method
50828      * is the runtime equivalent of setting the 'editable' config option at config time.
50829      * @param {Boolean} value True to allow the user to directly edit the field text
50830      */
50831     setEditable : function(value){
50832          
50833     },
50834
50835     // private
50836     onBeforeLoad : function(){
50837         
50838         Roo.log("Select before load");
50839         return;
50840     
50841         this.innerList.update(this.loadingText ?
50842                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
50843         //this.restrictHeight();
50844         this.selectedIndex = -1;
50845     },
50846
50847     // private
50848     onLoad : function(){
50849
50850     
50851         var dom = this.el.dom;
50852         dom.innerHTML = '';
50853          var od = dom.ownerDocument;
50854          
50855         if (this.emptyText) {
50856             var op = od.createElement('option');
50857             op.setAttribute('value', '');
50858             op.innerHTML = String.format('{0}', this.emptyText);
50859             dom.appendChild(op);
50860         }
50861         if(this.store.getCount() > 0){
50862            
50863             var vf = this.valueField;
50864             var df = this.displayField;
50865             this.store.data.each(function(r) {
50866                 // which colmsn to use... testing - cdoe / title..
50867                 var op = od.createElement('option');
50868                 op.setAttribute('value', r.data[vf]);
50869                 op.innerHTML = String.format('{0}', r.data[df]);
50870                 dom.appendChild(op);
50871             });
50872             if (typeof(this.defaultValue != 'undefined')) {
50873                 this.setValue(this.defaultValue);
50874             }
50875             
50876              
50877         }else{
50878             //this.onEmptyResults();
50879         }
50880         //this.el.focus();
50881     },
50882     // private
50883     onLoadException : function()
50884     {
50885         dom.innerHTML = '';
50886             
50887         Roo.log("Select on load exception");
50888         return;
50889     
50890         this.collapse();
50891         Roo.log(this.store.reader.jsonData);
50892         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
50893             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
50894         }
50895         
50896         
50897     },
50898     // private
50899     onTypeAhead : function(){
50900          
50901     },
50902
50903     // private
50904     onSelect : function(record, index){
50905         Roo.log('on select?');
50906         return;
50907         if(this.fireEvent('beforeselect', this, record, index) !== false){
50908             this.setFromData(index > -1 ? record.data : false);
50909             this.collapse();
50910             this.fireEvent('select', this, record, index);
50911         }
50912     },
50913
50914     /**
50915      * Returns the currently selected field value or empty string if no value is set.
50916      * @return {String} value The selected value
50917      */
50918     getValue : function(){
50919         var dom = this.el.dom;
50920         this.value = dom.options[dom.selectedIndex].value;
50921         return this.value;
50922         
50923     },
50924
50925     /**
50926      * Clears any text/value currently set in the field
50927      */
50928     clearValue : function(){
50929         this.value = '';
50930         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
50931         
50932     },
50933
50934     /**
50935      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
50936      * will be displayed in the field.  If the value does not match the data value of an existing item,
50937      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
50938      * Otherwise the field will be blank (although the value will still be set).
50939      * @param {String} value The value to match
50940      */
50941     setValue : function(v){
50942         var d = this.el.dom;
50943         for (var i =0; i < d.options.length;i++) {
50944             if (v == d.options[i].value) {
50945                 d.selectedIndex = i;
50946                 this.value = v;
50947                 return;
50948             }
50949         }
50950         this.clearValue();
50951     },
50952     /**
50953      * @property {Object} the last set data for the element
50954      */
50955     
50956     lastData : false,
50957     /**
50958      * Sets the value of the field based on a object which is related to the record format for the store.
50959      * @param {Object} value the value to set as. or false on reset?
50960      */
50961     setFromData : function(o){
50962         Roo.log('setfrom data?');
50963          
50964         
50965         
50966     },
50967     // private
50968     reset : function(){
50969         this.clearValue();
50970     },
50971     // private
50972     findRecord : function(prop, value){
50973         
50974         return false;
50975     
50976         var record;
50977         if(this.store.getCount() > 0){
50978             this.store.each(function(r){
50979                 if(r.data[prop] == value){
50980                     record = r;
50981                     return false;
50982                 }
50983                 return true;
50984             });
50985         }
50986         return record;
50987     },
50988     
50989     getName: function()
50990     {
50991         // returns hidden if it's set..
50992         if (!this.rendered) {return ''};
50993         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
50994         
50995     },
50996      
50997
50998     
50999
51000     // private
51001     onEmptyResults : function(){
51002         Roo.log('empty results');
51003         //this.collapse();
51004     },
51005
51006     /**
51007      * Returns true if the dropdown list is expanded, else false.
51008      */
51009     isExpanded : function(){
51010         return false;
51011     },
51012
51013     /**
51014      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51015      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51016      * @param {String} value The data value of the item to select
51017      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51018      * selected item if it is not currently in view (defaults to true)
51019      * @return {Boolean} True if the value matched an item in the list, else false
51020      */
51021     selectByValue : function(v, scrollIntoView){
51022         Roo.log('select By Value');
51023         return false;
51024     
51025         if(v !== undefined && v !== null){
51026             var r = this.findRecord(this.valueField || this.displayField, v);
51027             if(r){
51028                 this.select(this.store.indexOf(r), scrollIntoView);
51029                 return true;
51030             }
51031         }
51032         return false;
51033     },
51034
51035     /**
51036      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51037      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51038      * @param {Number} index The zero-based index of the list item to select
51039      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51040      * selected item if it is not currently in view (defaults to true)
51041      */
51042     select : function(index, scrollIntoView){
51043         Roo.log('select ');
51044         return  ;
51045         
51046         this.selectedIndex = index;
51047         this.view.select(index);
51048         if(scrollIntoView !== false){
51049             var el = this.view.getNode(index);
51050             if(el){
51051                 this.innerList.scrollChildIntoView(el, false);
51052             }
51053         }
51054     },
51055
51056       
51057
51058     // private
51059     validateBlur : function(){
51060         
51061         return;
51062         
51063     },
51064
51065     // private
51066     initQuery : function(){
51067         this.doQuery(this.getRawValue());
51068     },
51069
51070     // private
51071     doForce : function(){
51072         if(this.el.dom.value.length > 0){
51073             this.el.dom.value =
51074                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51075              
51076         }
51077     },
51078
51079     /**
51080      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51081      * query allowing the query action to be canceled if needed.
51082      * @param {String} query The SQL query to execute
51083      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51084      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51085      * saved in the current store (defaults to false)
51086      */
51087     doQuery : function(q, forceAll){
51088         
51089         Roo.log('doQuery?');
51090         if(q === undefined || q === null){
51091             q = '';
51092         }
51093         var qe = {
51094             query: q,
51095             forceAll: forceAll,
51096             combo: this,
51097             cancel:false
51098         };
51099         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51100             return false;
51101         }
51102         q = qe.query;
51103         forceAll = qe.forceAll;
51104         if(forceAll === true || (q.length >= this.minChars)){
51105             if(this.lastQuery != q || this.alwaysQuery){
51106                 this.lastQuery = q;
51107                 if(this.mode == 'local'){
51108                     this.selectedIndex = -1;
51109                     if(forceAll){
51110                         this.store.clearFilter();
51111                     }else{
51112                         this.store.filter(this.displayField, q);
51113                     }
51114                     this.onLoad();
51115                 }else{
51116                     this.store.baseParams[this.queryParam] = q;
51117                     this.store.load({
51118                         params: this.getParams(q)
51119                     });
51120                     this.expand();
51121                 }
51122             }else{
51123                 this.selectedIndex = -1;
51124                 this.onLoad();   
51125             }
51126         }
51127     },
51128
51129     // private
51130     getParams : function(q){
51131         var p = {};
51132         //p[this.queryParam] = q;
51133         if(this.pageSize){
51134             p.start = 0;
51135             p.limit = this.pageSize;
51136         }
51137         return p;
51138     },
51139
51140     /**
51141      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51142      */
51143     collapse : function(){
51144         
51145     },
51146
51147     // private
51148     collapseIf : function(e){
51149         
51150     },
51151
51152     /**
51153      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51154      */
51155     expand : function(){
51156         
51157     } ,
51158
51159     // private
51160      
51161
51162     /** 
51163     * @cfg {Boolean} grow 
51164     * @hide 
51165     */
51166     /** 
51167     * @cfg {Number} growMin 
51168     * @hide 
51169     */
51170     /** 
51171     * @cfg {Number} growMax 
51172     * @hide 
51173     */
51174     /**
51175      * @hide
51176      * @method autoSize
51177      */
51178     
51179     setWidth : function()
51180     {
51181         
51182     },
51183     getResizeEl : function(){
51184         return this.el;
51185     }
51186 });//<script type="text/javasscript">
51187  
51188
51189 /**
51190  * @class Roo.DDView
51191  * A DnD enabled version of Roo.View.
51192  * @param {Element/String} container The Element in which to create the View.
51193  * @param {String} tpl The template string used to create the markup for each element of the View
51194  * @param {Object} config The configuration properties. These include all the config options of
51195  * {@link Roo.View} plus some specific to this class.<br>
51196  * <p>
51197  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51198  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51199  * <p>
51200  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51201 .x-view-drag-insert-above {
51202         border-top:1px dotted #3366cc;
51203 }
51204 .x-view-drag-insert-below {
51205         border-bottom:1px dotted #3366cc;
51206 }
51207 </code></pre>
51208  * 
51209  */
51210  
51211 Roo.DDView = function(container, tpl, config) {
51212     Roo.DDView.superclass.constructor.apply(this, arguments);
51213     this.getEl().setStyle("outline", "0px none");
51214     this.getEl().unselectable();
51215     if (this.dragGroup) {
51216                 this.setDraggable(this.dragGroup.split(","));
51217     }
51218     if (this.dropGroup) {
51219                 this.setDroppable(this.dropGroup.split(","));
51220     }
51221     if (this.deletable) {
51222         this.setDeletable();
51223     }
51224     this.isDirtyFlag = false;
51225         this.addEvents({
51226                 "drop" : true
51227         });
51228 };
51229
51230 Roo.extend(Roo.DDView, Roo.View, {
51231 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51232 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51233 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51234 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51235
51236         isFormField: true,
51237
51238         reset: Roo.emptyFn,
51239         
51240         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51241
51242         validate: function() {
51243                 return true;
51244         },
51245         
51246         destroy: function() {
51247                 this.purgeListeners();
51248                 this.getEl.removeAllListeners();
51249                 this.getEl().remove();
51250                 if (this.dragZone) {
51251                         if (this.dragZone.destroy) {
51252                                 this.dragZone.destroy();
51253                         }
51254                 }
51255                 if (this.dropZone) {
51256                         if (this.dropZone.destroy) {
51257                                 this.dropZone.destroy();
51258                         }
51259                 }
51260         },
51261
51262 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51263         getName: function() {
51264                 return this.name;
51265         },
51266
51267 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51268         setValue: function(v) {
51269                 if (!this.store) {
51270                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51271                 }
51272                 var data = {};
51273                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51274                 this.store.proxy = new Roo.data.MemoryProxy(data);
51275                 this.store.load();
51276         },
51277
51278 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51279         getValue: function() {
51280                 var result = '(';
51281                 this.store.each(function(rec) {
51282                         result += rec.id + ',';
51283                 });
51284                 return result.substr(0, result.length - 1) + ')';
51285         },
51286         
51287         getIds: function() {
51288                 var i = 0, result = new Array(this.store.getCount());
51289                 this.store.each(function(rec) {
51290                         result[i++] = rec.id;
51291                 });
51292                 return result;
51293         },
51294         
51295         isDirty: function() {
51296                 return this.isDirtyFlag;
51297         },
51298
51299 /**
51300  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51301  *      whole Element becomes the target, and this causes the drop gesture to append.
51302  */
51303     getTargetFromEvent : function(e) {
51304                 var target = e.getTarget();
51305                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51306                 target = target.parentNode;
51307                 }
51308                 if (!target) {
51309                         target = this.el.dom.lastChild || this.el.dom;
51310                 }
51311                 return target;
51312     },
51313
51314 /**
51315  *      Create the drag data which consists of an object which has the property "ddel" as
51316  *      the drag proxy element. 
51317  */
51318     getDragData : function(e) {
51319         var target = this.findItemFromChild(e.getTarget());
51320                 if(target) {
51321                         this.handleSelection(e);
51322                         var selNodes = this.getSelectedNodes();
51323             var dragData = {
51324                 source: this,
51325                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51326                 nodes: selNodes,
51327                 records: []
51328                         };
51329                         var selectedIndices = this.getSelectedIndexes();
51330                         for (var i = 0; i < selectedIndices.length; i++) {
51331                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51332                         }
51333                         if (selNodes.length == 1) {
51334                                 dragData.ddel = target.cloneNode(true); // the div element
51335                         } else {
51336                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51337                                 div.className = 'multi-proxy';
51338                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51339                                         div.appendChild(selNodes[i].cloneNode(true));
51340                                 }
51341                                 dragData.ddel = div;
51342                         }
51343             //console.log(dragData)
51344             //console.log(dragData.ddel.innerHTML)
51345                         return dragData;
51346                 }
51347         //console.log('nodragData')
51348                 return false;
51349     },
51350     
51351 /**     Specify to which ddGroup items in this DDView may be dragged. */
51352     setDraggable: function(ddGroup) {
51353         if (ddGroup instanceof Array) {
51354                 Roo.each(ddGroup, this.setDraggable, this);
51355                 return;
51356         }
51357         if (this.dragZone) {
51358                 this.dragZone.addToGroup(ddGroup);
51359         } else {
51360                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51361                                 containerScroll: true,
51362                                 ddGroup: ddGroup 
51363
51364                         });
51365 //                      Draggability implies selection. DragZone's mousedown selects the element.
51366                         if (!this.multiSelect) { this.singleSelect = true; }
51367
51368 //                      Wire the DragZone's handlers up to methods in *this*
51369                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51370                 }
51371     },
51372
51373 /**     Specify from which ddGroup this DDView accepts drops. */
51374     setDroppable: function(ddGroup) {
51375         if (ddGroup instanceof Array) {
51376                 Roo.each(ddGroup, this.setDroppable, this);
51377                 return;
51378         }
51379         if (this.dropZone) {
51380                 this.dropZone.addToGroup(ddGroup);
51381         } else {
51382                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51383                                 containerScroll: true,
51384                                 ddGroup: ddGroup
51385                         });
51386
51387 //                      Wire the DropZone's handlers up to methods in *this*
51388                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51389                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51390                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51391                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51392                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51393                 }
51394     },
51395
51396 /**     Decide whether to drop above or below a View node. */
51397     getDropPoint : function(e, n, dd){
51398         if (n == this.el.dom) { return "above"; }
51399                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51400                 var c = t + (b - t) / 2;
51401                 var y = Roo.lib.Event.getPageY(e);
51402                 if(y <= c) {
51403                         return "above";
51404                 }else{
51405                         return "below";
51406                 }
51407     },
51408
51409     onNodeEnter : function(n, dd, e, data){
51410                 return false;
51411     },
51412     
51413     onNodeOver : function(n, dd, e, data){
51414                 var pt = this.getDropPoint(e, n, dd);
51415                 // set the insert point style on the target node
51416                 var dragElClass = this.dropNotAllowed;
51417                 if (pt) {
51418                         var targetElClass;
51419                         if (pt == "above"){
51420                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51421                                 targetElClass = "x-view-drag-insert-above";
51422                         } else {
51423                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51424                                 targetElClass = "x-view-drag-insert-below";
51425                         }
51426                         if (this.lastInsertClass != targetElClass){
51427                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51428                                 this.lastInsertClass = targetElClass;
51429                         }
51430                 }
51431                 return dragElClass;
51432         },
51433
51434     onNodeOut : function(n, dd, e, data){
51435                 this.removeDropIndicators(n);
51436     },
51437
51438     onNodeDrop : function(n, dd, e, data){
51439         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51440                 return false;
51441         }
51442         var pt = this.getDropPoint(e, n, dd);
51443                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51444                 if (pt == "below") { insertAt++; }
51445                 for (var i = 0; i < data.records.length; i++) {
51446                         var r = data.records[i];
51447                         var dup = this.store.getById(r.id);
51448                         if (dup && (dd != this.dragZone)) {
51449                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51450                         } else {
51451                                 if (data.copy) {
51452                                         this.store.insert(insertAt++, r.copy());
51453                                 } else {
51454                                         data.source.isDirtyFlag = true;
51455                                         r.store.remove(r);
51456                                         this.store.insert(insertAt++, r);
51457                                 }
51458                                 this.isDirtyFlag = true;
51459                         }
51460                 }
51461                 this.dragZone.cachedTarget = null;
51462                 return true;
51463     },
51464
51465     removeDropIndicators : function(n){
51466                 if(n){
51467                         Roo.fly(n).removeClass([
51468                                 "x-view-drag-insert-above",
51469                                 "x-view-drag-insert-below"]);
51470                         this.lastInsertClass = "_noclass";
51471                 }
51472     },
51473
51474 /**
51475  *      Utility method. Add a delete option to the DDView's context menu.
51476  *      @param {String} imageUrl The URL of the "delete" icon image.
51477  */
51478         setDeletable: function(imageUrl) {
51479                 if (!this.singleSelect && !this.multiSelect) {
51480                         this.singleSelect = true;
51481                 }
51482                 var c = this.getContextMenu();
51483                 this.contextMenu.on("itemclick", function(item) {
51484                         switch (item.id) {
51485                                 case "delete":
51486                                         this.remove(this.getSelectedIndexes());
51487                                         break;
51488                         }
51489                 }, this);
51490                 this.contextMenu.add({
51491                         icon: imageUrl,
51492                         id: "delete",
51493                         text: 'Delete'
51494                 });
51495         },
51496         
51497 /**     Return the context menu for this DDView. */
51498         getContextMenu: function() {
51499                 if (!this.contextMenu) {
51500 //                      Create the View's context menu
51501                         this.contextMenu = new Roo.menu.Menu({
51502                                 id: this.id + "-contextmenu"
51503                         });
51504                         this.el.on("contextmenu", this.showContextMenu, this);
51505                 }
51506                 return this.contextMenu;
51507         },
51508         
51509         disableContextMenu: function() {
51510                 if (this.contextMenu) {
51511                         this.el.un("contextmenu", this.showContextMenu, this);
51512                 }
51513         },
51514
51515         showContextMenu: function(e, item) {
51516         item = this.findItemFromChild(e.getTarget());
51517                 if (item) {
51518                         e.stopEvent();
51519                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51520                         this.contextMenu.showAt(e.getXY());
51521             }
51522     },
51523
51524 /**
51525  *      Remove {@link Roo.data.Record}s at the specified indices.
51526  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51527  */
51528     remove: function(selectedIndices) {
51529                 selectedIndices = [].concat(selectedIndices);
51530                 for (var i = 0; i < selectedIndices.length; i++) {
51531                         var rec = this.store.getAt(selectedIndices[i]);
51532                         this.store.remove(rec);
51533                 }
51534     },
51535
51536 /**
51537  *      Double click fires the event, but also, if this is draggable, and there is only one other
51538  *      related DropZone, it transfers the selected node.
51539  */
51540     onDblClick : function(e){
51541         var item = this.findItemFromChild(e.getTarget());
51542         if(item){
51543             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51544                 return false;
51545             }
51546             if (this.dragGroup) {
51547                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51548                     while (targets.indexOf(this.dropZone) > -1) {
51549                             targets.remove(this.dropZone);
51550                                 }
51551                     if (targets.length == 1) {
51552                                         this.dragZone.cachedTarget = null;
51553                         var el = Roo.get(targets[0].getEl());
51554                         var box = el.getBox(true);
51555                         targets[0].onNodeDrop(el.dom, {
51556                                 target: el.dom,
51557                                 xy: [box.x, box.y + box.height - 1]
51558                         }, null, this.getDragData(e));
51559                     }
51560                 }
51561         }
51562     },
51563     
51564     handleSelection: function(e) {
51565                 this.dragZone.cachedTarget = null;
51566         var item = this.findItemFromChild(e.getTarget());
51567         if (!item) {
51568                 this.clearSelections(true);
51569                 return;
51570         }
51571                 if (item && (this.multiSelect || this.singleSelect)){
51572                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51573                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51574                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51575                                 this.unselect(item);
51576                         } else {
51577                                 this.select(item, this.multiSelect && e.ctrlKey);
51578                                 this.lastSelection = item;
51579                         }
51580                 }
51581     },
51582
51583     onItemClick : function(item, index, e){
51584                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51585                         return false;
51586                 }
51587                 return true;
51588     },
51589
51590     unselect : function(nodeInfo, suppressEvent){
51591                 var node = this.getNode(nodeInfo);
51592                 if(node && this.isSelected(node)){
51593                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51594                                 Roo.fly(node).removeClass(this.selectedClass);
51595                                 this.selections.remove(node);
51596                                 if(!suppressEvent){
51597                                         this.fireEvent("selectionchange", this, this.selections);
51598                                 }
51599                         }
51600                 }
51601     }
51602 });
51603 /*
51604  * Based on:
51605  * Ext JS Library 1.1.1
51606  * Copyright(c) 2006-2007, Ext JS, LLC.
51607  *
51608  * Originally Released Under LGPL - original licence link has changed is not relivant.
51609  *
51610  * Fork - LGPL
51611  * <script type="text/javascript">
51612  */
51613  
51614 /**
51615  * @class Roo.LayoutManager
51616  * @extends Roo.util.Observable
51617  * Base class for layout managers.
51618  */
51619 Roo.LayoutManager = function(container, config){
51620     Roo.LayoutManager.superclass.constructor.call(this);
51621     this.el = Roo.get(container);
51622     // ie scrollbar fix
51623     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51624         document.body.scroll = "no";
51625     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51626         this.el.position('relative');
51627     }
51628     this.id = this.el.id;
51629     this.el.addClass("x-layout-container");
51630     /** false to disable window resize monitoring @type Boolean */
51631     this.monitorWindowResize = true;
51632     this.regions = {};
51633     this.addEvents({
51634         /**
51635          * @event layout
51636          * Fires when a layout is performed. 
51637          * @param {Roo.LayoutManager} this
51638          */
51639         "layout" : true,
51640         /**
51641          * @event regionresized
51642          * Fires when the user resizes a region. 
51643          * @param {Roo.LayoutRegion} region The resized region
51644          * @param {Number} newSize The new size (width for east/west, height for north/south)
51645          */
51646         "regionresized" : true,
51647         /**
51648          * @event regioncollapsed
51649          * Fires when a region is collapsed. 
51650          * @param {Roo.LayoutRegion} region The collapsed region
51651          */
51652         "regioncollapsed" : true,
51653         /**
51654          * @event regionexpanded
51655          * Fires when a region is expanded.  
51656          * @param {Roo.LayoutRegion} region The expanded region
51657          */
51658         "regionexpanded" : true
51659     });
51660     this.updating = false;
51661     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
51662 };
51663
51664 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
51665     /**
51666      * Returns true if this layout is currently being updated
51667      * @return {Boolean}
51668      */
51669     isUpdating : function(){
51670         return this.updating; 
51671     },
51672     
51673     /**
51674      * Suspend the LayoutManager from doing auto-layouts while
51675      * making multiple add or remove calls
51676      */
51677     beginUpdate : function(){
51678         this.updating = true;    
51679     },
51680     
51681     /**
51682      * Restore auto-layouts and optionally disable the manager from performing a layout
51683      * @param {Boolean} noLayout true to disable a layout update 
51684      */
51685     endUpdate : function(noLayout){
51686         this.updating = false;
51687         if(!noLayout){
51688             this.layout();
51689         }    
51690     },
51691     
51692     layout: function(){
51693         
51694     },
51695     
51696     onRegionResized : function(region, newSize){
51697         this.fireEvent("regionresized", region, newSize);
51698         this.layout();
51699     },
51700     
51701     onRegionCollapsed : function(region){
51702         this.fireEvent("regioncollapsed", region);
51703     },
51704     
51705     onRegionExpanded : function(region){
51706         this.fireEvent("regionexpanded", region);
51707     },
51708         
51709     /**
51710      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
51711      * performs box-model adjustments.
51712      * @return {Object} The size as an object {width: (the width), height: (the height)}
51713      */
51714     getViewSize : function(){
51715         var size;
51716         if(this.el.dom != document.body){
51717             size = this.el.getSize();
51718         }else{
51719             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
51720         }
51721         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
51722         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
51723         return size;
51724     },
51725     
51726     /**
51727      * Returns the Element this layout is bound to.
51728      * @return {Roo.Element}
51729      */
51730     getEl : function(){
51731         return this.el;
51732     },
51733     
51734     /**
51735      * Returns the specified region.
51736      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
51737      * @return {Roo.LayoutRegion}
51738      */
51739     getRegion : function(target){
51740         return this.regions[target.toLowerCase()];
51741     },
51742     
51743     onWindowResize : function(){
51744         if(this.monitorWindowResize){
51745             this.layout();
51746         }
51747     }
51748 });/*
51749  * Based on:
51750  * Ext JS Library 1.1.1
51751  * Copyright(c) 2006-2007, Ext JS, LLC.
51752  *
51753  * Originally Released Under LGPL - original licence link has changed is not relivant.
51754  *
51755  * Fork - LGPL
51756  * <script type="text/javascript">
51757  */
51758 /**
51759  * @class Roo.BorderLayout
51760  * @extends Roo.LayoutManager
51761  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
51762  * please see: <br><br>
51763  * <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>
51764  * <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>
51765  * Example:
51766  <pre><code>
51767  var layout = new Roo.BorderLayout(document.body, {
51768     north: {
51769         initialSize: 25,
51770         titlebar: false
51771     },
51772     west: {
51773         split:true,
51774         initialSize: 200,
51775         minSize: 175,
51776         maxSize: 400,
51777         titlebar: true,
51778         collapsible: true
51779     },
51780     east: {
51781         split:true,
51782         initialSize: 202,
51783         minSize: 175,
51784         maxSize: 400,
51785         titlebar: true,
51786         collapsible: true
51787     },
51788     south: {
51789         split:true,
51790         initialSize: 100,
51791         minSize: 100,
51792         maxSize: 200,
51793         titlebar: true,
51794         collapsible: true
51795     },
51796     center: {
51797         titlebar: true,
51798         autoScroll:true,
51799         resizeTabs: true,
51800         minTabWidth: 50,
51801         preferredTabWidth: 150
51802     }
51803 });
51804
51805 // shorthand
51806 var CP = Roo.ContentPanel;
51807
51808 layout.beginUpdate();
51809 layout.add("north", new CP("north", "North"));
51810 layout.add("south", new CP("south", {title: "South", closable: true}));
51811 layout.add("west", new CP("west", {title: "West"}));
51812 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
51813 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
51814 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
51815 layout.getRegion("center").showPanel("center1");
51816 layout.endUpdate();
51817 </code></pre>
51818
51819 <b>The container the layout is rendered into can be either the body element or any other element.
51820 If it is not the body element, the container needs to either be an absolute positioned element,
51821 or you will need to add "position:relative" to the css of the container.  You will also need to specify
51822 the container size if it is not the body element.</b>
51823
51824 * @constructor
51825 * Create a new BorderLayout
51826 * @param {String/HTMLElement/Element} container The container this layout is bound to
51827 * @param {Object} config Configuration options
51828  */
51829 Roo.BorderLayout = function(container, config){
51830     config = config || {};
51831     Roo.BorderLayout.superclass.constructor.call(this, container, config);
51832     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
51833     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
51834         var target = this.factory.validRegions[i];
51835         if(config[target]){
51836             this.addRegion(target, config[target]);
51837         }
51838     }
51839 };
51840
51841 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
51842     /**
51843      * Creates and adds a new region if it doesn't already exist.
51844      * @param {String} target The target region key (north, south, east, west or center).
51845      * @param {Object} config The regions config object
51846      * @return {BorderLayoutRegion} The new region
51847      */
51848     addRegion : function(target, config){
51849         if(!this.regions[target]){
51850             var r = this.factory.create(target, this, config);
51851             this.bindRegion(target, r);
51852         }
51853         return this.regions[target];
51854     },
51855
51856     // private (kinda)
51857     bindRegion : function(name, r){
51858         this.regions[name] = r;
51859         r.on("visibilitychange", this.layout, this);
51860         r.on("paneladded", this.layout, this);
51861         r.on("panelremoved", this.layout, this);
51862         r.on("invalidated", this.layout, this);
51863         r.on("resized", this.onRegionResized, this);
51864         r.on("collapsed", this.onRegionCollapsed, this);
51865         r.on("expanded", this.onRegionExpanded, this);
51866     },
51867
51868     /**
51869      * Performs a layout update.
51870      */
51871     layout : function(){
51872         if(this.updating) {
51873             return;
51874         }
51875         var size = this.getViewSize();
51876         var w = size.width;
51877         var h = size.height;
51878         var centerW = w;
51879         var centerH = h;
51880         var centerY = 0;
51881         var centerX = 0;
51882         //var x = 0, y = 0;
51883
51884         var rs = this.regions;
51885         var north = rs["north"];
51886         var south = rs["south"]; 
51887         var west = rs["west"];
51888         var east = rs["east"];
51889         var center = rs["center"];
51890         //if(this.hideOnLayout){ // not supported anymore
51891             //c.el.setStyle("display", "none");
51892         //}
51893         if(north && north.isVisible()){
51894             var b = north.getBox();
51895             var m = north.getMargins();
51896             b.width = w - (m.left+m.right);
51897             b.x = m.left;
51898             b.y = m.top;
51899             centerY = b.height + b.y + m.bottom;
51900             centerH -= centerY;
51901             north.updateBox(this.safeBox(b));
51902         }
51903         if(south && south.isVisible()){
51904             var b = south.getBox();
51905             var m = south.getMargins();
51906             b.width = w - (m.left+m.right);
51907             b.x = m.left;
51908             var totalHeight = (b.height + m.top + m.bottom);
51909             b.y = h - totalHeight + m.top;
51910             centerH -= totalHeight;
51911             south.updateBox(this.safeBox(b));
51912         }
51913         if(west && west.isVisible()){
51914             var b = west.getBox();
51915             var m = west.getMargins();
51916             b.height = centerH - (m.top+m.bottom);
51917             b.x = m.left;
51918             b.y = centerY + m.top;
51919             var totalWidth = (b.width + m.left + m.right);
51920             centerX += totalWidth;
51921             centerW -= totalWidth;
51922             west.updateBox(this.safeBox(b));
51923         }
51924         if(east && east.isVisible()){
51925             var b = east.getBox();
51926             var m = east.getMargins();
51927             b.height = centerH - (m.top+m.bottom);
51928             var totalWidth = (b.width + m.left + m.right);
51929             b.x = w - totalWidth + m.left;
51930             b.y = centerY + m.top;
51931             centerW -= totalWidth;
51932             east.updateBox(this.safeBox(b));
51933         }
51934         if(center){
51935             var m = center.getMargins();
51936             var centerBox = {
51937                 x: centerX + m.left,
51938                 y: centerY + m.top,
51939                 width: centerW - (m.left+m.right),
51940                 height: centerH - (m.top+m.bottom)
51941             };
51942             //if(this.hideOnLayout){
51943                 //center.el.setStyle("display", "block");
51944             //}
51945             center.updateBox(this.safeBox(centerBox));
51946         }
51947         this.el.repaint();
51948         this.fireEvent("layout", this);
51949     },
51950
51951     // private
51952     safeBox : function(box){
51953         box.width = Math.max(0, box.width);
51954         box.height = Math.max(0, box.height);
51955         return box;
51956     },
51957
51958     /**
51959      * Adds a ContentPanel (or subclass) to this layout.
51960      * @param {String} target The target region key (north, south, east, west or center).
51961      * @param {Roo.ContentPanel} panel The panel to add
51962      * @return {Roo.ContentPanel} The added panel
51963      */
51964     add : function(target, panel){
51965          
51966         target = target.toLowerCase();
51967         return this.regions[target].add(panel);
51968     },
51969
51970     /**
51971      * Remove a ContentPanel (or subclass) to this layout.
51972      * @param {String} target The target region key (north, south, east, west or center).
51973      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
51974      * @return {Roo.ContentPanel} The removed panel
51975      */
51976     remove : function(target, panel){
51977         target = target.toLowerCase();
51978         return this.regions[target].remove(panel);
51979     },
51980
51981     /**
51982      * Searches all regions for a panel with the specified id
51983      * @param {String} panelId
51984      * @return {Roo.ContentPanel} The panel or null if it wasn't found
51985      */
51986     findPanel : function(panelId){
51987         var rs = this.regions;
51988         for(var target in rs){
51989             if(typeof rs[target] != "function"){
51990                 var p = rs[target].getPanel(panelId);
51991                 if(p){
51992                     return p;
51993                 }
51994             }
51995         }
51996         return null;
51997     },
51998
51999     /**
52000      * Searches all regions for a panel with the specified id and activates (shows) it.
52001      * @param {String/ContentPanel} panelId The panels id or the panel itself
52002      * @return {Roo.ContentPanel} The shown panel or null
52003      */
52004     showPanel : function(panelId) {
52005       var rs = this.regions;
52006       for(var target in rs){
52007          var r = rs[target];
52008          if(typeof r != "function"){
52009             if(r.hasPanel(panelId)){
52010                return r.showPanel(panelId);
52011             }
52012          }
52013       }
52014       return null;
52015    },
52016
52017    /**
52018      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52019      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52020      */
52021     restoreState : function(provider){
52022         if(!provider){
52023             provider = Roo.state.Manager;
52024         }
52025         var sm = new Roo.LayoutStateManager();
52026         sm.init(this, provider);
52027     },
52028
52029     /**
52030      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52031      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52032      * a valid ContentPanel config object.  Example:
52033      * <pre><code>
52034 // Create the main layout
52035 var layout = new Roo.BorderLayout('main-ct', {
52036     west: {
52037         split:true,
52038         minSize: 175,
52039         titlebar: true
52040     },
52041     center: {
52042         title:'Components'
52043     }
52044 }, 'main-ct');
52045
52046 // Create and add multiple ContentPanels at once via configs
52047 layout.batchAdd({
52048    west: {
52049        id: 'source-files',
52050        autoCreate:true,
52051        title:'Ext Source Files',
52052        autoScroll:true,
52053        fitToFrame:true
52054    },
52055    center : {
52056        el: cview,
52057        autoScroll:true,
52058        fitToFrame:true,
52059        toolbar: tb,
52060        resizeEl:'cbody'
52061    }
52062 });
52063 </code></pre>
52064      * @param {Object} regions An object containing ContentPanel configs by region name
52065      */
52066     batchAdd : function(regions){
52067         this.beginUpdate();
52068         for(var rname in regions){
52069             var lr = this.regions[rname];
52070             if(lr){
52071                 this.addTypedPanels(lr, regions[rname]);
52072             }
52073         }
52074         this.endUpdate();
52075     },
52076
52077     // private
52078     addTypedPanels : function(lr, ps){
52079         if(typeof ps == 'string'){
52080             lr.add(new Roo.ContentPanel(ps));
52081         }
52082         else if(ps instanceof Array){
52083             for(var i =0, len = ps.length; i < len; i++){
52084                 this.addTypedPanels(lr, ps[i]);
52085             }
52086         }
52087         else if(!ps.events){ // raw config?
52088             var el = ps.el;
52089             delete ps.el; // prevent conflict
52090             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52091         }
52092         else {  // panel object assumed!
52093             lr.add(ps);
52094         }
52095     },
52096     /**
52097      * Adds a xtype elements to the layout.
52098      * <pre><code>
52099
52100 layout.addxtype({
52101        xtype : 'ContentPanel',
52102        region: 'west',
52103        items: [ .... ]
52104    }
52105 );
52106
52107 layout.addxtype({
52108         xtype : 'NestedLayoutPanel',
52109         region: 'west',
52110         layout: {
52111            center: { },
52112            west: { }   
52113         },
52114         items : [ ... list of content panels or nested layout panels.. ]
52115    }
52116 );
52117 </code></pre>
52118      * @param {Object} cfg Xtype definition of item to add.
52119      */
52120     addxtype : function(cfg)
52121     {
52122         // basically accepts a pannel...
52123         // can accept a layout region..!?!?
52124         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52125         
52126         if (!cfg.xtype.match(/Panel$/)) {
52127             return false;
52128         }
52129         var ret = false;
52130         
52131         if (typeof(cfg.region) == 'undefined') {
52132             Roo.log("Failed to add Panel, region was not set");
52133             Roo.log(cfg);
52134             return false;
52135         }
52136         var region = cfg.region;
52137         delete cfg.region;
52138         
52139           
52140         var xitems = [];
52141         if (cfg.items) {
52142             xitems = cfg.items;
52143             delete cfg.items;
52144         }
52145         var nb = false;
52146         
52147         switch(cfg.xtype) 
52148         {
52149             case 'ContentPanel':  // ContentPanel (el, cfg)
52150             case 'ScrollPanel':  // ContentPanel (el, cfg)
52151             case 'ViewPanel': 
52152                 if(cfg.autoCreate) {
52153                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52154                 } else {
52155                     var el = this.el.createChild();
52156                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52157                 }
52158                 
52159                 this.add(region, ret);
52160                 break;
52161             
52162             
52163             case 'TreePanel': // our new panel!
52164                 cfg.el = this.el.createChild();
52165                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52166                 this.add(region, ret);
52167                 break;
52168             
52169             case 'NestedLayoutPanel': 
52170                 // create a new Layout (which is  a Border Layout...
52171                 var el = this.el.createChild();
52172                 var clayout = cfg.layout;
52173                 delete cfg.layout;
52174                 clayout.items   = clayout.items  || [];
52175                 // replace this exitems with the clayout ones..
52176                 xitems = clayout.items;
52177                  
52178                 
52179                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52180                     cfg.background = false;
52181                 }
52182                 var layout = new Roo.BorderLayout(el, clayout);
52183                 
52184                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52185                 //console.log('adding nested layout panel '  + cfg.toSource());
52186                 this.add(region, ret);
52187                 nb = {}; /// find first...
52188                 break;
52189                 
52190             case 'GridPanel': 
52191             
52192                 // needs grid and region
52193                 
52194                 //var el = this.getRegion(region).el.createChild();
52195                 var el = this.el.createChild();
52196                 // create the grid first...
52197                 
52198                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52199                 delete cfg.grid;
52200                 if (region == 'center' && this.active ) {
52201                     cfg.background = false;
52202                 }
52203                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52204                 
52205                 this.add(region, ret);
52206                 if (cfg.background) {
52207                     ret.on('activate', function(gp) {
52208                         if (!gp.grid.rendered) {
52209                             gp.grid.render();
52210                         }
52211                     });
52212                 } else {
52213                     grid.render();
52214                 }
52215                 break;
52216            
52217            
52218            
52219                 
52220                 
52221                 
52222             default:
52223                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52224                     
52225                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52226                     this.add(region, ret);
52227                 } else {
52228                 
52229                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52230                     return null;
52231                 }
52232                 
52233              // GridPanel (grid, cfg)
52234             
52235         }
52236         this.beginUpdate();
52237         // add children..
52238         var region = '';
52239         var abn = {};
52240         Roo.each(xitems, function(i)  {
52241             region = nb && i.region ? i.region : false;
52242             
52243             var add = ret.addxtype(i);
52244            
52245             if (region) {
52246                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52247                 if (!i.background) {
52248                     abn[region] = nb[region] ;
52249                 }
52250             }
52251             
52252         });
52253         this.endUpdate();
52254
52255         // make the last non-background panel active..
52256         //if (nb) { Roo.log(abn); }
52257         if (nb) {
52258             
52259             for(var r in abn) {
52260                 region = this.getRegion(r);
52261                 if (region) {
52262                     // tried using nb[r], but it does not work..
52263                      
52264                     region.showPanel(abn[r]);
52265                    
52266                 }
52267             }
52268         }
52269         return ret;
52270         
52271     }
52272 });
52273
52274 /**
52275  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52276  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52277  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52278  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52279  * <pre><code>
52280 // shorthand
52281 var CP = Roo.ContentPanel;
52282
52283 var layout = Roo.BorderLayout.create({
52284     north: {
52285         initialSize: 25,
52286         titlebar: false,
52287         panels: [new CP("north", "North")]
52288     },
52289     west: {
52290         split:true,
52291         initialSize: 200,
52292         minSize: 175,
52293         maxSize: 400,
52294         titlebar: true,
52295         collapsible: true,
52296         panels: [new CP("west", {title: "West"})]
52297     },
52298     east: {
52299         split:true,
52300         initialSize: 202,
52301         minSize: 175,
52302         maxSize: 400,
52303         titlebar: true,
52304         collapsible: true,
52305         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52306     },
52307     south: {
52308         split:true,
52309         initialSize: 100,
52310         minSize: 100,
52311         maxSize: 200,
52312         titlebar: true,
52313         collapsible: true,
52314         panels: [new CP("south", {title: "South", closable: true})]
52315     },
52316     center: {
52317         titlebar: true,
52318         autoScroll:true,
52319         resizeTabs: true,
52320         minTabWidth: 50,
52321         preferredTabWidth: 150,
52322         panels: [
52323             new CP("center1", {title: "Close Me", closable: true}),
52324             new CP("center2", {title: "Center Panel", closable: false})
52325         ]
52326     }
52327 }, document.body);
52328
52329 layout.getRegion("center").showPanel("center1");
52330 </code></pre>
52331  * @param config
52332  * @param targetEl
52333  */
52334 Roo.BorderLayout.create = function(config, targetEl){
52335     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52336     layout.beginUpdate();
52337     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52338     for(var j = 0, jlen = regions.length; j < jlen; j++){
52339         var lr = regions[j];
52340         if(layout.regions[lr] && config[lr].panels){
52341             var r = layout.regions[lr];
52342             var ps = config[lr].panels;
52343             layout.addTypedPanels(r, ps);
52344         }
52345     }
52346     layout.endUpdate();
52347     return layout;
52348 };
52349
52350 // private
52351 Roo.BorderLayout.RegionFactory = {
52352     // private
52353     validRegions : ["north","south","east","west","center"],
52354
52355     // private
52356     create : function(target, mgr, config){
52357         target = target.toLowerCase();
52358         if(config.lightweight || config.basic){
52359             return new Roo.BasicLayoutRegion(mgr, config, target);
52360         }
52361         switch(target){
52362             case "north":
52363                 return new Roo.NorthLayoutRegion(mgr, config);
52364             case "south":
52365                 return new Roo.SouthLayoutRegion(mgr, config);
52366             case "east":
52367                 return new Roo.EastLayoutRegion(mgr, config);
52368             case "west":
52369                 return new Roo.WestLayoutRegion(mgr, config);
52370             case "center":
52371                 return new Roo.CenterLayoutRegion(mgr, config);
52372         }
52373         throw 'Layout region "'+target+'" not supported.';
52374     }
52375 };/*
52376  * Based on:
52377  * Ext JS Library 1.1.1
52378  * Copyright(c) 2006-2007, Ext JS, LLC.
52379  *
52380  * Originally Released Under LGPL - original licence link has changed is not relivant.
52381  *
52382  * Fork - LGPL
52383  * <script type="text/javascript">
52384  */
52385  
52386 /**
52387  * @class Roo.BasicLayoutRegion
52388  * @extends Roo.util.Observable
52389  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52390  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52391  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52392  */
52393 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52394     this.mgr = mgr;
52395     this.position  = pos;
52396     this.events = {
52397         /**
52398          * @scope Roo.BasicLayoutRegion
52399          */
52400         
52401         /**
52402          * @event beforeremove
52403          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52404          * @param {Roo.LayoutRegion} this
52405          * @param {Roo.ContentPanel} panel The panel
52406          * @param {Object} e The cancel event object
52407          */
52408         "beforeremove" : true,
52409         /**
52410          * @event invalidated
52411          * Fires when the layout for this region is changed.
52412          * @param {Roo.LayoutRegion} this
52413          */
52414         "invalidated" : true,
52415         /**
52416          * @event visibilitychange
52417          * Fires when this region is shown or hidden 
52418          * @param {Roo.LayoutRegion} this
52419          * @param {Boolean} visibility true or false
52420          */
52421         "visibilitychange" : true,
52422         /**
52423          * @event paneladded
52424          * Fires when a panel is added. 
52425          * @param {Roo.LayoutRegion} this
52426          * @param {Roo.ContentPanel} panel The panel
52427          */
52428         "paneladded" : true,
52429         /**
52430          * @event panelremoved
52431          * Fires when a panel is removed. 
52432          * @param {Roo.LayoutRegion} this
52433          * @param {Roo.ContentPanel} panel The panel
52434          */
52435         "panelremoved" : true,
52436         /**
52437          * @event beforecollapse
52438          * Fires when this region before collapse.
52439          * @param {Roo.LayoutRegion} this
52440          */
52441         "beforecollapse" : true,
52442         /**
52443          * @event collapsed
52444          * Fires when this region is collapsed.
52445          * @param {Roo.LayoutRegion} this
52446          */
52447         "collapsed" : true,
52448         /**
52449          * @event expanded
52450          * Fires when this region is expanded.
52451          * @param {Roo.LayoutRegion} this
52452          */
52453         "expanded" : true,
52454         /**
52455          * @event slideshow
52456          * Fires when this region is slid into view.
52457          * @param {Roo.LayoutRegion} this
52458          */
52459         "slideshow" : true,
52460         /**
52461          * @event slidehide
52462          * Fires when this region slides out of view. 
52463          * @param {Roo.LayoutRegion} this
52464          */
52465         "slidehide" : true,
52466         /**
52467          * @event panelactivated
52468          * Fires when a panel is activated. 
52469          * @param {Roo.LayoutRegion} this
52470          * @param {Roo.ContentPanel} panel The activated panel
52471          */
52472         "panelactivated" : true,
52473         /**
52474          * @event resized
52475          * Fires when the user resizes this region. 
52476          * @param {Roo.LayoutRegion} this
52477          * @param {Number} newSize The new size (width for east/west, height for north/south)
52478          */
52479         "resized" : true
52480     };
52481     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52482     this.panels = new Roo.util.MixedCollection();
52483     this.panels.getKey = this.getPanelId.createDelegate(this);
52484     this.box = null;
52485     this.activePanel = null;
52486     // ensure listeners are added...
52487     
52488     if (config.listeners || config.events) {
52489         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52490             listeners : config.listeners || {},
52491             events : config.events || {}
52492         });
52493     }
52494     
52495     if(skipConfig !== true){
52496         this.applyConfig(config);
52497     }
52498 };
52499
52500 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52501     getPanelId : function(p){
52502         return p.getId();
52503     },
52504     
52505     applyConfig : function(config){
52506         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52507         this.config = config;
52508         
52509     },
52510     
52511     /**
52512      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52513      * the width, for horizontal (north, south) the height.
52514      * @param {Number} newSize The new width or height
52515      */
52516     resizeTo : function(newSize){
52517         var el = this.el ? this.el :
52518                  (this.activePanel ? this.activePanel.getEl() : null);
52519         if(el){
52520             switch(this.position){
52521                 case "east":
52522                 case "west":
52523                     el.setWidth(newSize);
52524                     this.fireEvent("resized", this, newSize);
52525                 break;
52526                 case "north":
52527                 case "south":
52528                     el.setHeight(newSize);
52529                     this.fireEvent("resized", this, newSize);
52530                 break;                
52531             }
52532         }
52533     },
52534     
52535     getBox : function(){
52536         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52537     },
52538     
52539     getMargins : function(){
52540         return this.margins;
52541     },
52542     
52543     updateBox : function(box){
52544         this.box = box;
52545         var el = this.activePanel.getEl();
52546         el.dom.style.left = box.x + "px";
52547         el.dom.style.top = box.y + "px";
52548         this.activePanel.setSize(box.width, box.height);
52549     },
52550     
52551     /**
52552      * Returns the container element for this region.
52553      * @return {Roo.Element}
52554      */
52555     getEl : function(){
52556         return this.activePanel;
52557     },
52558     
52559     /**
52560      * Returns true if this region is currently visible.
52561      * @return {Boolean}
52562      */
52563     isVisible : function(){
52564         return this.activePanel ? true : false;
52565     },
52566     
52567     setActivePanel : function(panel){
52568         panel = this.getPanel(panel);
52569         if(this.activePanel && this.activePanel != panel){
52570             this.activePanel.setActiveState(false);
52571             this.activePanel.getEl().setLeftTop(-10000,-10000);
52572         }
52573         this.activePanel = panel;
52574         panel.setActiveState(true);
52575         if(this.box){
52576             panel.setSize(this.box.width, this.box.height);
52577         }
52578         this.fireEvent("panelactivated", this, panel);
52579         this.fireEvent("invalidated");
52580     },
52581     
52582     /**
52583      * Show the specified panel.
52584      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52585      * @return {Roo.ContentPanel} The shown panel or null
52586      */
52587     showPanel : function(panel){
52588         if(panel = this.getPanel(panel)){
52589             this.setActivePanel(panel);
52590         }
52591         return panel;
52592     },
52593     
52594     /**
52595      * Get the active panel for this region.
52596      * @return {Roo.ContentPanel} The active panel or null
52597      */
52598     getActivePanel : function(){
52599         return this.activePanel;
52600     },
52601     
52602     /**
52603      * Add the passed ContentPanel(s)
52604      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52605      * @return {Roo.ContentPanel} The panel added (if only one was added)
52606      */
52607     add : function(panel){
52608         if(arguments.length > 1){
52609             for(var i = 0, len = arguments.length; i < len; i++) {
52610                 this.add(arguments[i]);
52611             }
52612             return null;
52613         }
52614         if(this.hasPanel(panel)){
52615             this.showPanel(panel);
52616             return panel;
52617         }
52618         var el = panel.getEl();
52619         if(el.dom.parentNode != this.mgr.el.dom){
52620             this.mgr.el.dom.appendChild(el.dom);
52621         }
52622         if(panel.setRegion){
52623             panel.setRegion(this);
52624         }
52625         this.panels.add(panel);
52626         el.setStyle("position", "absolute");
52627         if(!panel.background){
52628             this.setActivePanel(panel);
52629             if(this.config.initialSize && this.panels.getCount()==1){
52630                 this.resizeTo(this.config.initialSize);
52631             }
52632         }
52633         this.fireEvent("paneladded", this, panel);
52634         return panel;
52635     },
52636     
52637     /**
52638      * Returns true if the panel is in this region.
52639      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52640      * @return {Boolean}
52641      */
52642     hasPanel : function(panel){
52643         if(typeof panel == "object"){ // must be panel obj
52644             panel = panel.getId();
52645         }
52646         return this.getPanel(panel) ? true : false;
52647     },
52648     
52649     /**
52650      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
52651      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52652      * @param {Boolean} preservePanel Overrides the config preservePanel option
52653      * @return {Roo.ContentPanel} The panel that was removed
52654      */
52655     remove : function(panel, preservePanel){
52656         panel = this.getPanel(panel);
52657         if(!panel){
52658             return null;
52659         }
52660         var e = {};
52661         this.fireEvent("beforeremove", this, panel, e);
52662         if(e.cancel === true){
52663             return null;
52664         }
52665         var panelId = panel.getId();
52666         this.panels.removeKey(panelId);
52667         return panel;
52668     },
52669     
52670     /**
52671      * Returns the panel specified or null if it's not in this region.
52672      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52673      * @return {Roo.ContentPanel}
52674      */
52675     getPanel : function(id){
52676         if(typeof id == "object"){ // must be panel obj
52677             return id;
52678         }
52679         return this.panels.get(id);
52680     },
52681     
52682     /**
52683      * Returns this regions position (north/south/east/west/center).
52684      * @return {String} 
52685      */
52686     getPosition: function(){
52687         return this.position;    
52688     }
52689 });/*
52690  * Based on:
52691  * Ext JS Library 1.1.1
52692  * Copyright(c) 2006-2007, Ext JS, LLC.
52693  *
52694  * Originally Released Under LGPL - original licence link has changed is not relivant.
52695  *
52696  * Fork - LGPL
52697  * <script type="text/javascript">
52698  */
52699  
52700 /**
52701  * @class Roo.LayoutRegion
52702  * @extends Roo.BasicLayoutRegion
52703  * This class represents a region in a layout manager.
52704  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
52705  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
52706  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
52707  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
52708  * @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})
52709  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
52710  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
52711  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
52712  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
52713  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
52714  * @cfg {String}    title           The title for the region (overrides panel titles)
52715  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
52716  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
52717  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
52718  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
52719  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
52720  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
52721  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
52722  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
52723  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
52724  * @cfg {Boolean}   showPin         True to show a pin button
52725  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
52726  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
52727  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
52728  * @cfg {Number}    width           For East/West panels
52729  * @cfg {Number}    height          For North/South panels
52730  * @cfg {Boolean}   split           To show the splitter
52731  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
52732  */
52733 Roo.LayoutRegion = function(mgr, config, pos){
52734     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
52735     var dh = Roo.DomHelper;
52736     /** This region's container element 
52737     * @type Roo.Element */
52738     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
52739     /** This region's title element 
52740     * @type Roo.Element */
52741
52742     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
52743         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
52744         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
52745     ]}, true);
52746     this.titleEl.enableDisplayMode();
52747     /** This region's title text element 
52748     * @type HTMLElement */
52749     this.titleTextEl = this.titleEl.dom.firstChild;
52750     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
52751     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
52752     this.closeBtn.enableDisplayMode();
52753     this.closeBtn.on("click", this.closeClicked, this);
52754     this.closeBtn.hide();
52755
52756     this.createBody(config);
52757     this.visible = true;
52758     this.collapsed = false;
52759
52760     if(config.hideWhenEmpty){
52761         this.hide();
52762         this.on("paneladded", this.validateVisibility, this);
52763         this.on("panelremoved", this.validateVisibility, this);
52764     }
52765     this.applyConfig(config);
52766 };
52767
52768 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
52769
52770     createBody : function(){
52771         /** This region's body element 
52772         * @type Roo.Element */
52773         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
52774     },
52775
52776     applyConfig : function(c){
52777         if(c.collapsible && this.position != "center" && !this.collapsedEl){
52778             var dh = Roo.DomHelper;
52779             if(c.titlebar !== false){
52780                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
52781                 this.collapseBtn.on("click", this.collapse, this);
52782                 this.collapseBtn.enableDisplayMode();
52783
52784                 if(c.showPin === true || this.showPin){
52785                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
52786                     this.stickBtn.enableDisplayMode();
52787                     this.stickBtn.on("click", this.expand, this);
52788                     this.stickBtn.hide();
52789                 }
52790             }
52791             /** This region's collapsed element
52792             * @type Roo.Element */
52793             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
52794                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
52795             ]}, true);
52796             if(c.floatable !== false){
52797                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
52798                this.collapsedEl.on("click", this.collapseClick, this);
52799             }
52800
52801             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
52802                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
52803                    id: "message", unselectable: "on", style:{"float":"left"}});
52804                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
52805              }
52806             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
52807             this.expandBtn.on("click", this.expand, this);
52808         }
52809         if(this.collapseBtn){
52810             this.collapseBtn.setVisible(c.collapsible == true);
52811         }
52812         this.cmargins = c.cmargins || this.cmargins ||
52813                          (this.position == "west" || this.position == "east" ?
52814                              {top: 0, left: 2, right:2, bottom: 0} :
52815                              {top: 2, left: 0, right:0, bottom: 2});
52816         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52817         this.bottomTabs = c.tabPosition != "top";
52818         this.autoScroll = c.autoScroll || false;
52819         if(this.autoScroll){
52820             this.bodyEl.setStyle("overflow", "auto");
52821         }else{
52822             this.bodyEl.setStyle("overflow", "hidden");
52823         }
52824         //if(c.titlebar !== false){
52825             if((!c.titlebar && !c.title) || c.titlebar === false){
52826                 this.titleEl.hide();
52827             }else{
52828                 this.titleEl.show();
52829                 if(c.title){
52830                     this.titleTextEl.innerHTML = c.title;
52831                 }
52832             }
52833         //}
52834         this.duration = c.duration || .30;
52835         this.slideDuration = c.slideDuration || .45;
52836         this.config = c;
52837         if(c.collapsed){
52838             this.collapse(true);
52839         }
52840         if(c.hidden){
52841             this.hide();
52842         }
52843     },
52844     /**
52845      * Returns true if this region is currently visible.
52846      * @return {Boolean}
52847      */
52848     isVisible : function(){
52849         return this.visible;
52850     },
52851
52852     /**
52853      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
52854      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
52855      */
52856     setCollapsedTitle : function(title){
52857         title = title || "&#160;";
52858         if(this.collapsedTitleTextEl){
52859             this.collapsedTitleTextEl.innerHTML = title;
52860         }
52861     },
52862
52863     getBox : function(){
52864         var b;
52865         if(!this.collapsed){
52866             b = this.el.getBox(false, true);
52867         }else{
52868             b = this.collapsedEl.getBox(false, true);
52869         }
52870         return b;
52871     },
52872
52873     getMargins : function(){
52874         return this.collapsed ? this.cmargins : this.margins;
52875     },
52876
52877     highlight : function(){
52878         this.el.addClass("x-layout-panel-dragover");
52879     },
52880
52881     unhighlight : function(){
52882         this.el.removeClass("x-layout-panel-dragover");
52883     },
52884
52885     updateBox : function(box){
52886         this.box = box;
52887         if(!this.collapsed){
52888             this.el.dom.style.left = box.x + "px";
52889             this.el.dom.style.top = box.y + "px";
52890             this.updateBody(box.width, box.height);
52891         }else{
52892             this.collapsedEl.dom.style.left = box.x + "px";
52893             this.collapsedEl.dom.style.top = box.y + "px";
52894             this.collapsedEl.setSize(box.width, box.height);
52895         }
52896         if(this.tabs){
52897             this.tabs.autoSizeTabs();
52898         }
52899     },
52900
52901     updateBody : function(w, h){
52902         if(w !== null){
52903             this.el.setWidth(w);
52904             w -= this.el.getBorderWidth("rl");
52905             if(this.config.adjustments){
52906                 w += this.config.adjustments[0];
52907             }
52908         }
52909         if(h !== null){
52910             this.el.setHeight(h);
52911             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
52912             h -= this.el.getBorderWidth("tb");
52913             if(this.config.adjustments){
52914                 h += this.config.adjustments[1];
52915             }
52916             this.bodyEl.setHeight(h);
52917             if(this.tabs){
52918                 h = this.tabs.syncHeight(h);
52919             }
52920         }
52921         if(this.panelSize){
52922             w = w !== null ? w : this.panelSize.width;
52923             h = h !== null ? h : this.panelSize.height;
52924         }
52925         if(this.activePanel){
52926             var el = this.activePanel.getEl();
52927             w = w !== null ? w : el.getWidth();
52928             h = h !== null ? h : el.getHeight();
52929             this.panelSize = {width: w, height: h};
52930             this.activePanel.setSize(w, h);
52931         }
52932         if(Roo.isIE && this.tabs){
52933             this.tabs.el.repaint();
52934         }
52935     },
52936
52937     /**
52938      * Returns the container element for this region.
52939      * @return {Roo.Element}
52940      */
52941     getEl : function(){
52942         return this.el;
52943     },
52944
52945     /**
52946      * Hides this region.
52947      */
52948     hide : function(){
52949         if(!this.collapsed){
52950             this.el.dom.style.left = "-2000px";
52951             this.el.hide();
52952         }else{
52953             this.collapsedEl.dom.style.left = "-2000px";
52954             this.collapsedEl.hide();
52955         }
52956         this.visible = false;
52957         this.fireEvent("visibilitychange", this, false);
52958     },
52959
52960     /**
52961      * Shows this region if it was previously hidden.
52962      */
52963     show : function(){
52964         if(!this.collapsed){
52965             this.el.show();
52966         }else{
52967             this.collapsedEl.show();
52968         }
52969         this.visible = true;
52970         this.fireEvent("visibilitychange", this, true);
52971     },
52972
52973     closeClicked : function(){
52974         if(this.activePanel){
52975             this.remove(this.activePanel);
52976         }
52977     },
52978
52979     collapseClick : function(e){
52980         if(this.isSlid){
52981            e.stopPropagation();
52982            this.slideIn();
52983         }else{
52984            e.stopPropagation();
52985            this.slideOut();
52986         }
52987     },
52988
52989     /**
52990      * Collapses this region.
52991      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
52992      */
52993     collapse : function(skipAnim, skipCheck){
52994         if(this.collapsed) {
52995             return;
52996         }
52997         
52998         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
52999             
53000             this.collapsed = true;
53001             if(this.split){
53002                 this.split.el.hide();
53003             }
53004             if(this.config.animate && skipAnim !== true){
53005                 this.fireEvent("invalidated", this);
53006                 this.animateCollapse();
53007             }else{
53008                 this.el.setLocation(-20000,-20000);
53009                 this.el.hide();
53010                 this.collapsedEl.show();
53011                 this.fireEvent("collapsed", this);
53012                 this.fireEvent("invalidated", this);
53013             }
53014         }
53015         
53016     },
53017
53018     animateCollapse : function(){
53019         // overridden
53020     },
53021
53022     /**
53023      * Expands this region if it was previously collapsed.
53024      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53025      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53026      */
53027     expand : function(e, skipAnim){
53028         if(e) {
53029             e.stopPropagation();
53030         }
53031         if(!this.collapsed || this.el.hasActiveFx()) {
53032             return;
53033         }
53034         if(this.isSlid){
53035             this.afterSlideIn();
53036             skipAnim = true;
53037         }
53038         this.collapsed = false;
53039         if(this.config.animate && skipAnim !== true){
53040             this.animateExpand();
53041         }else{
53042             this.el.show();
53043             if(this.split){
53044                 this.split.el.show();
53045             }
53046             this.collapsedEl.setLocation(-2000,-2000);
53047             this.collapsedEl.hide();
53048             this.fireEvent("invalidated", this);
53049             this.fireEvent("expanded", this);
53050         }
53051     },
53052
53053     animateExpand : function(){
53054         // overridden
53055     },
53056
53057     initTabs : function()
53058     {
53059         this.bodyEl.setStyle("overflow", "hidden");
53060         var ts = new Roo.TabPanel(
53061                 this.bodyEl.dom,
53062                 {
53063                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53064                     disableTooltips: this.config.disableTabTips,
53065                     toolbar : this.config.toolbar
53066                 }
53067         );
53068         if(this.config.hideTabs){
53069             ts.stripWrap.setDisplayed(false);
53070         }
53071         this.tabs = ts;
53072         ts.resizeTabs = this.config.resizeTabs === true;
53073         ts.minTabWidth = this.config.minTabWidth || 40;
53074         ts.maxTabWidth = this.config.maxTabWidth || 250;
53075         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53076         ts.monitorResize = false;
53077         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53078         ts.bodyEl.addClass('x-layout-tabs-body');
53079         this.panels.each(this.initPanelAsTab, this);
53080     },
53081
53082     initPanelAsTab : function(panel){
53083         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53084                     this.config.closeOnTab && panel.isClosable());
53085         if(panel.tabTip !== undefined){
53086             ti.setTooltip(panel.tabTip);
53087         }
53088         ti.on("activate", function(){
53089               this.setActivePanel(panel);
53090         }, this);
53091         if(this.config.closeOnTab){
53092             ti.on("beforeclose", function(t, e){
53093                 e.cancel = true;
53094                 this.remove(panel);
53095             }, this);
53096         }
53097         return ti;
53098     },
53099
53100     updatePanelTitle : function(panel, title){
53101         if(this.activePanel == panel){
53102             this.updateTitle(title);
53103         }
53104         if(this.tabs){
53105             var ti = this.tabs.getTab(panel.getEl().id);
53106             ti.setText(title);
53107             if(panel.tabTip !== undefined){
53108                 ti.setTooltip(panel.tabTip);
53109             }
53110         }
53111     },
53112
53113     updateTitle : function(title){
53114         if(this.titleTextEl && !this.config.title){
53115             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53116         }
53117     },
53118
53119     setActivePanel : function(panel){
53120         panel = this.getPanel(panel);
53121         if(this.activePanel && this.activePanel != panel){
53122             this.activePanel.setActiveState(false);
53123         }
53124         this.activePanel = panel;
53125         panel.setActiveState(true);
53126         if(this.panelSize){
53127             panel.setSize(this.panelSize.width, this.panelSize.height);
53128         }
53129         if(this.closeBtn){
53130             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53131         }
53132         this.updateTitle(panel.getTitle());
53133         if(this.tabs){
53134             this.fireEvent("invalidated", this);
53135         }
53136         this.fireEvent("panelactivated", this, panel);
53137     },
53138
53139     /**
53140      * Shows the specified panel.
53141      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53142      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53143      */
53144     showPanel : function(panel)
53145     {
53146         panel = this.getPanel(panel);
53147         if(panel){
53148             if(this.tabs){
53149                 var tab = this.tabs.getTab(panel.getEl().id);
53150                 if(tab.isHidden()){
53151                     this.tabs.unhideTab(tab.id);
53152                 }
53153                 tab.activate();
53154             }else{
53155                 this.setActivePanel(panel);
53156             }
53157         }
53158         return panel;
53159     },
53160
53161     /**
53162      * Get the active panel for this region.
53163      * @return {Roo.ContentPanel} The active panel or null
53164      */
53165     getActivePanel : function(){
53166         return this.activePanel;
53167     },
53168
53169     validateVisibility : function(){
53170         if(this.panels.getCount() < 1){
53171             this.updateTitle("&#160;");
53172             this.closeBtn.hide();
53173             this.hide();
53174         }else{
53175             if(!this.isVisible()){
53176                 this.show();
53177             }
53178         }
53179     },
53180
53181     /**
53182      * Adds the passed ContentPanel(s) to this region.
53183      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53184      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53185      */
53186     add : function(panel){
53187         if(arguments.length > 1){
53188             for(var i = 0, len = arguments.length; i < len; i++) {
53189                 this.add(arguments[i]);
53190             }
53191             return null;
53192         }
53193         if(this.hasPanel(panel)){
53194             this.showPanel(panel);
53195             return panel;
53196         }
53197         panel.setRegion(this);
53198         this.panels.add(panel);
53199         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53200             this.bodyEl.dom.appendChild(panel.getEl().dom);
53201             if(panel.background !== true){
53202                 this.setActivePanel(panel);
53203             }
53204             this.fireEvent("paneladded", this, panel);
53205             return panel;
53206         }
53207         if(!this.tabs){
53208             this.initTabs();
53209         }else{
53210             this.initPanelAsTab(panel);
53211         }
53212         if(panel.background !== true){
53213             this.tabs.activate(panel.getEl().id);
53214         }
53215         this.fireEvent("paneladded", this, panel);
53216         return panel;
53217     },
53218
53219     /**
53220      * Hides the tab for the specified panel.
53221      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53222      */
53223     hidePanel : function(panel){
53224         if(this.tabs && (panel = this.getPanel(panel))){
53225             this.tabs.hideTab(panel.getEl().id);
53226         }
53227     },
53228
53229     /**
53230      * Unhides the tab for a previously hidden panel.
53231      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53232      */
53233     unhidePanel : function(panel){
53234         if(this.tabs && (panel = this.getPanel(panel))){
53235             this.tabs.unhideTab(panel.getEl().id);
53236         }
53237     },
53238
53239     clearPanels : function(){
53240         while(this.panels.getCount() > 0){
53241              this.remove(this.panels.first());
53242         }
53243     },
53244
53245     /**
53246      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53247      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53248      * @param {Boolean} preservePanel Overrides the config preservePanel option
53249      * @return {Roo.ContentPanel} The panel that was removed
53250      */
53251     remove : function(panel, preservePanel){
53252         panel = this.getPanel(panel);
53253         if(!panel){
53254             return null;
53255         }
53256         var e = {};
53257         this.fireEvent("beforeremove", this, panel, e);
53258         if(e.cancel === true){
53259             return null;
53260         }
53261         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53262         var panelId = panel.getId();
53263         this.panels.removeKey(panelId);
53264         if(preservePanel){
53265             document.body.appendChild(panel.getEl().dom);
53266         }
53267         if(this.tabs){
53268             this.tabs.removeTab(panel.getEl().id);
53269         }else if (!preservePanel){
53270             this.bodyEl.dom.removeChild(panel.getEl().dom);
53271         }
53272         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53273             var p = this.panels.first();
53274             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53275             tempEl.appendChild(p.getEl().dom);
53276             this.bodyEl.update("");
53277             this.bodyEl.dom.appendChild(p.getEl().dom);
53278             tempEl = null;
53279             this.updateTitle(p.getTitle());
53280             this.tabs = null;
53281             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53282             this.setActivePanel(p);
53283         }
53284         panel.setRegion(null);
53285         if(this.activePanel == panel){
53286             this.activePanel = null;
53287         }
53288         if(this.config.autoDestroy !== false && preservePanel !== true){
53289             try{panel.destroy();}catch(e){}
53290         }
53291         this.fireEvent("panelremoved", this, panel);
53292         return panel;
53293     },
53294
53295     /**
53296      * Returns the TabPanel component used by this region
53297      * @return {Roo.TabPanel}
53298      */
53299     getTabs : function(){
53300         return this.tabs;
53301     },
53302
53303     createTool : function(parentEl, className){
53304         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53305             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53306         btn.addClassOnOver("x-layout-tools-button-over");
53307         return btn;
53308     }
53309 });/*
53310  * Based on:
53311  * Ext JS Library 1.1.1
53312  * Copyright(c) 2006-2007, Ext JS, LLC.
53313  *
53314  * Originally Released Under LGPL - original licence link has changed is not relivant.
53315  *
53316  * Fork - LGPL
53317  * <script type="text/javascript">
53318  */
53319  
53320
53321
53322 /**
53323  * @class Roo.SplitLayoutRegion
53324  * @extends Roo.LayoutRegion
53325  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53326  */
53327 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53328     this.cursor = cursor;
53329     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53330 };
53331
53332 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53333     splitTip : "Drag to resize.",
53334     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53335     useSplitTips : false,
53336
53337     applyConfig : function(config){
53338         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53339         if(config.split){
53340             if(!this.split){
53341                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53342                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53343                 /** The SplitBar for this region 
53344                 * @type Roo.SplitBar */
53345                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53346                 this.split.on("moved", this.onSplitMove, this);
53347                 this.split.useShim = config.useShim === true;
53348                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53349                 if(this.useSplitTips){
53350                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53351                 }
53352                 if(config.collapsible){
53353                     this.split.el.on("dblclick", this.collapse,  this);
53354                 }
53355             }
53356             if(typeof config.minSize != "undefined"){
53357                 this.split.minSize = config.minSize;
53358             }
53359             if(typeof config.maxSize != "undefined"){
53360                 this.split.maxSize = config.maxSize;
53361             }
53362             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53363                 this.hideSplitter();
53364             }
53365         }
53366     },
53367
53368     getHMaxSize : function(){
53369          var cmax = this.config.maxSize || 10000;
53370          var center = this.mgr.getRegion("center");
53371          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53372     },
53373
53374     getVMaxSize : function(){
53375          var cmax = this.config.maxSize || 10000;
53376          var center = this.mgr.getRegion("center");
53377          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53378     },
53379
53380     onSplitMove : function(split, newSize){
53381         this.fireEvent("resized", this, newSize);
53382     },
53383     
53384     /** 
53385      * Returns the {@link Roo.SplitBar} for this region.
53386      * @return {Roo.SplitBar}
53387      */
53388     getSplitBar : function(){
53389         return this.split;
53390     },
53391     
53392     hide : function(){
53393         this.hideSplitter();
53394         Roo.SplitLayoutRegion.superclass.hide.call(this);
53395     },
53396
53397     hideSplitter : function(){
53398         if(this.split){
53399             this.split.el.setLocation(-2000,-2000);
53400             this.split.el.hide();
53401         }
53402     },
53403
53404     show : function(){
53405         if(this.split){
53406             this.split.el.show();
53407         }
53408         Roo.SplitLayoutRegion.superclass.show.call(this);
53409     },
53410     
53411     beforeSlide: function(){
53412         if(Roo.isGecko){// firefox overflow auto bug workaround
53413             this.bodyEl.clip();
53414             if(this.tabs) {
53415                 this.tabs.bodyEl.clip();
53416             }
53417             if(this.activePanel){
53418                 this.activePanel.getEl().clip();
53419                 
53420                 if(this.activePanel.beforeSlide){
53421                     this.activePanel.beforeSlide();
53422                 }
53423             }
53424         }
53425     },
53426     
53427     afterSlide : function(){
53428         if(Roo.isGecko){// firefox overflow auto bug workaround
53429             this.bodyEl.unclip();
53430             if(this.tabs) {
53431                 this.tabs.bodyEl.unclip();
53432             }
53433             if(this.activePanel){
53434                 this.activePanel.getEl().unclip();
53435                 if(this.activePanel.afterSlide){
53436                     this.activePanel.afterSlide();
53437                 }
53438             }
53439         }
53440     },
53441
53442     initAutoHide : function(){
53443         if(this.autoHide !== false){
53444             if(!this.autoHideHd){
53445                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53446                 this.autoHideHd = {
53447                     "mouseout": function(e){
53448                         if(!e.within(this.el, true)){
53449                             st.delay(500);
53450                         }
53451                     },
53452                     "mouseover" : function(e){
53453                         st.cancel();
53454                     },
53455                     scope : this
53456                 };
53457             }
53458             this.el.on(this.autoHideHd);
53459         }
53460     },
53461
53462     clearAutoHide : function(){
53463         if(this.autoHide !== false){
53464             this.el.un("mouseout", this.autoHideHd.mouseout);
53465             this.el.un("mouseover", this.autoHideHd.mouseover);
53466         }
53467     },
53468
53469     clearMonitor : function(){
53470         Roo.get(document).un("click", this.slideInIf, this);
53471     },
53472
53473     // these names are backwards but not changed for compat
53474     slideOut : function(){
53475         if(this.isSlid || this.el.hasActiveFx()){
53476             return;
53477         }
53478         this.isSlid = true;
53479         if(this.collapseBtn){
53480             this.collapseBtn.hide();
53481         }
53482         this.closeBtnState = this.closeBtn.getStyle('display');
53483         this.closeBtn.hide();
53484         if(this.stickBtn){
53485             this.stickBtn.show();
53486         }
53487         this.el.show();
53488         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53489         this.beforeSlide();
53490         this.el.setStyle("z-index", 10001);
53491         this.el.slideIn(this.getSlideAnchor(), {
53492             callback: function(){
53493                 this.afterSlide();
53494                 this.initAutoHide();
53495                 Roo.get(document).on("click", this.slideInIf, this);
53496                 this.fireEvent("slideshow", this);
53497             },
53498             scope: this,
53499             block: true
53500         });
53501     },
53502
53503     afterSlideIn : function(){
53504         this.clearAutoHide();
53505         this.isSlid = false;
53506         this.clearMonitor();
53507         this.el.setStyle("z-index", "");
53508         if(this.collapseBtn){
53509             this.collapseBtn.show();
53510         }
53511         this.closeBtn.setStyle('display', this.closeBtnState);
53512         if(this.stickBtn){
53513             this.stickBtn.hide();
53514         }
53515         this.fireEvent("slidehide", this);
53516     },
53517
53518     slideIn : function(cb){
53519         if(!this.isSlid || this.el.hasActiveFx()){
53520             Roo.callback(cb);
53521             return;
53522         }
53523         this.isSlid = false;
53524         this.beforeSlide();
53525         this.el.slideOut(this.getSlideAnchor(), {
53526             callback: function(){
53527                 this.el.setLeftTop(-10000, -10000);
53528                 this.afterSlide();
53529                 this.afterSlideIn();
53530                 Roo.callback(cb);
53531             },
53532             scope: this,
53533             block: true
53534         });
53535     },
53536     
53537     slideInIf : function(e){
53538         if(!e.within(this.el)){
53539             this.slideIn();
53540         }
53541     },
53542
53543     animateCollapse : function(){
53544         this.beforeSlide();
53545         this.el.setStyle("z-index", 20000);
53546         var anchor = this.getSlideAnchor();
53547         this.el.slideOut(anchor, {
53548             callback : function(){
53549                 this.el.setStyle("z-index", "");
53550                 this.collapsedEl.slideIn(anchor, {duration:.3});
53551                 this.afterSlide();
53552                 this.el.setLocation(-10000,-10000);
53553                 this.el.hide();
53554                 this.fireEvent("collapsed", this);
53555             },
53556             scope: this,
53557             block: true
53558         });
53559     },
53560
53561     animateExpand : function(){
53562         this.beforeSlide();
53563         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53564         this.el.setStyle("z-index", 20000);
53565         this.collapsedEl.hide({
53566             duration:.1
53567         });
53568         this.el.slideIn(this.getSlideAnchor(), {
53569             callback : function(){
53570                 this.el.setStyle("z-index", "");
53571                 this.afterSlide();
53572                 if(this.split){
53573                     this.split.el.show();
53574                 }
53575                 this.fireEvent("invalidated", this);
53576                 this.fireEvent("expanded", this);
53577             },
53578             scope: this,
53579             block: true
53580         });
53581     },
53582
53583     anchors : {
53584         "west" : "left",
53585         "east" : "right",
53586         "north" : "top",
53587         "south" : "bottom"
53588     },
53589
53590     sanchors : {
53591         "west" : "l",
53592         "east" : "r",
53593         "north" : "t",
53594         "south" : "b"
53595     },
53596
53597     canchors : {
53598         "west" : "tl-tr",
53599         "east" : "tr-tl",
53600         "north" : "tl-bl",
53601         "south" : "bl-tl"
53602     },
53603
53604     getAnchor : function(){
53605         return this.anchors[this.position];
53606     },
53607
53608     getCollapseAnchor : function(){
53609         return this.canchors[this.position];
53610     },
53611
53612     getSlideAnchor : function(){
53613         return this.sanchors[this.position];
53614     },
53615
53616     getAlignAdj : function(){
53617         var cm = this.cmargins;
53618         switch(this.position){
53619             case "west":
53620                 return [0, 0];
53621             break;
53622             case "east":
53623                 return [0, 0];
53624             break;
53625             case "north":
53626                 return [0, 0];
53627             break;
53628             case "south":
53629                 return [0, 0];
53630             break;
53631         }
53632     },
53633
53634     getExpandAdj : function(){
53635         var c = this.collapsedEl, cm = this.cmargins;
53636         switch(this.position){
53637             case "west":
53638                 return [-(cm.right+c.getWidth()+cm.left), 0];
53639             break;
53640             case "east":
53641                 return [cm.right+c.getWidth()+cm.left, 0];
53642             break;
53643             case "north":
53644                 return [0, -(cm.top+cm.bottom+c.getHeight())];
53645             break;
53646             case "south":
53647                 return [0, cm.top+cm.bottom+c.getHeight()];
53648             break;
53649         }
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  * These classes are private internal classes
53663  */
53664 Roo.CenterLayoutRegion = function(mgr, config){
53665     Roo.LayoutRegion.call(this, mgr, config, "center");
53666     this.visible = true;
53667     this.minWidth = config.minWidth || 20;
53668     this.minHeight = config.minHeight || 20;
53669 };
53670
53671 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
53672     hide : function(){
53673         // center panel can't be hidden
53674     },
53675     
53676     show : function(){
53677         // center panel can't be hidden
53678     },
53679     
53680     getMinWidth: function(){
53681         return this.minWidth;
53682     },
53683     
53684     getMinHeight: function(){
53685         return this.minHeight;
53686     }
53687 });
53688
53689
53690 Roo.NorthLayoutRegion = function(mgr, config){
53691     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
53692     if(this.split){
53693         this.split.placement = Roo.SplitBar.TOP;
53694         this.split.orientation = Roo.SplitBar.VERTICAL;
53695         this.split.el.addClass("x-layout-split-v");
53696     }
53697     var size = config.initialSize || config.height;
53698     if(typeof size != "undefined"){
53699         this.el.setHeight(size);
53700     }
53701 };
53702 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
53703     orientation: Roo.SplitBar.VERTICAL,
53704     getBox : function(){
53705         if(this.collapsed){
53706             return this.collapsedEl.getBox();
53707         }
53708         var box = this.el.getBox();
53709         if(this.split){
53710             box.height += this.split.el.getHeight();
53711         }
53712         return box;
53713     },
53714     
53715     updateBox : function(box){
53716         if(this.split && !this.collapsed){
53717             box.height -= this.split.el.getHeight();
53718             this.split.el.setLeft(box.x);
53719             this.split.el.setTop(box.y+box.height);
53720             this.split.el.setWidth(box.width);
53721         }
53722         if(this.collapsed){
53723             this.updateBody(box.width, null);
53724         }
53725         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53726     }
53727 });
53728
53729 Roo.SouthLayoutRegion = function(mgr, config){
53730     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
53731     if(this.split){
53732         this.split.placement = Roo.SplitBar.BOTTOM;
53733         this.split.orientation = Roo.SplitBar.VERTICAL;
53734         this.split.el.addClass("x-layout-split-v");
53735     }
53736     var size = config.initialSize || config.height;
53737     if(typeof size != "undefined"){
53738         this.el.setHeight(size);
53739     }
53740 };
53741 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
53742     orientation: Roo.SplitBar.VERTICAL,
53743     getBox : function(){
53744         if(this.collapsed){
53745             return this.collapsedEl.getBox();
53746         }
53747         var box = this.el.getBox();
53748         if(this.split){
53749             var sh = this.split.el.getHeight();
53750             box.height += sh;
53751             box.y -= sh;
53752         }
53753         return box;
53754     },
53755     
53756     updateBox : function(box){
53757         if(this.split && !this.collapsed){
53758             var sh = this.split.el.getHeight();
53759             box.height -= sh;
53760             box.y += sh;
53761             this.split.el.setLeft(box.x);
53762             this.split.el.setTop(box.y-sh);
53763             this.split.el.setWidth(box.width);
53764         }
53765         if(this.collapsed){
53766             this.updateBody(box.width, null);
53767         }
53768         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53769     }
53770 });
53771
53772 Roo.EastLayoutRegion = function(mgr, config){
53773     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
53774     if(this.split){
53775         this.split.placement = Roo.SplitBar.RIGHT;
53776         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53777         this.split.el.addClass("x-layout-split-h");
53778     }
53779     var size = config.initialSize || config.width;
53780     if(typeof size != "undefined"){
53781         this.el.setWidth(size);
53782     }
53783 };
53784 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
53785     orientation: Roo.SplitBar.HORIZONTAL,
53786     getBox : function(){
53787         if(this.collapsed){
53788             return this.collapsedEl.getBox();
53789         }
53790         var box = this.el.getBox();
53791         if(this.split){
53792             var sw = this.split.el.getWidth();
53793             box.width += sw;
53794             box.x -= sw;
53795         }
53796         return box;
53797     },
53798
53799     updateBox : function(box){
53800         if(this.split && !this.collapsed){
53801             var sw = this.split.el.getWidth();
53802             box.width -= sw;
53803             this.split.el.setLeft(box.x);
53804             this.split.el.setTop(box.y);
53805             this.split.el.setHeight(box.height);
53806             box.x += sw;
53807         }
53808         if(this.collapsed){
53809             this.updateBody(null, box.height);
53810         }
53811         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53812     }
53813 });
53814
53815 Roo.WestLayoutRegion = function(mgr, config){
53816     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
53817     if(this.split){
53818         this.split.placement = Roo.SplitBar.LEFT;
53819         this.split.orientation = Roo.SplitBar.HORIZONTAL;
53820         this.split.el.addClass("x-layout-split-h");
53821     }
53822     var size = config.initialSize || config.width;
53823     if(typeof size != "undefined"){
53824         this.el.setWidth(size);
53825     }
53826 };
53827 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
53828     orientation: Roo.SplitBar.HORIZONTAL,
53829     getBox : function(){
53830         if(this.collapsed){
53831             return this.collapsedEl.getBox();
53832         }
53833         var box = this.el.getBox();
53834         if(this.split){
53835             box.width += this.split.el.getWidth();
53836         }
53837         return box;
53838     },
53839     
53840     updateBox : function(box){
53841         if(this.split && !this.collapsed){
53842             var sw = this.split.el.getWidth();
53843             box.width -= sw;
53844             this.split.el.setLeft(box.x+box.width);
53845             this.split.el.setTop(box.y);
53846             this.split.el.setHeight(box.height);
53847         }
53848         if(this.collapsed){
53849             this.updateBody(null, box.height);
53850         }
53851         Roo.LayoutRegion.prototype.updateBox.call(this, box);
53852     }
53853 });
53854 /*
53855  * Based on:
53856  * Ext JS Library 1.1.1
53857  * Copyright(c) 2006-2007, Ext JS, LLC.
53858  *
53859  * Originally Released Under LGPL - original licence link has changed is not relivant.
53860  *
53861  * Fork - LGPL
53862  * <script type="text/javascript">
53863  */
53864  
53865  
53866 /*
53867  * Private internal class for reading and applying state
53868  */
53869 Roo.LayoutStateManager = function(layout){
53870      // default empty state
53871      this.state = {
53872         north: {},
53873         south: {},
53874         east: {},
53875         west: {}       
53876     };
53877 };
53878
53879 Roo.LayoutStateManager.prototype = {
53880     init : function(layout, provider){
53881         this.provider = provider;
53882         var state = provider.get(layout.id+"-layout-state");
53883         if(state){
53884             var wasUpdating = layout.isUpdating();
53885             if(!wasUpdating){
53886                 layout.beginUpdate();
53887             }
53888             for(var key in state){
53889                 if(typeof state[key] != "function"){
53890                     var rstate = state[key];
53891                     var r = layout.getRegion(key);
53892                     if(r && rstate){
53893                         if(rstate.size){
53894                             r.resizeTo(rstate.size);
53895                         }
53896                         if(rstate.collapsed == true){
53897                             r.collapse(true);
53898                         }else{
53899                             r.expand(null, true);
53900                         }
53901                     }
53902                 }
53903             }
53904             if(!wasUpdating){
53905                 layout.endUpdate();
53906             }
53907             this.state = state; 
53908         }
53909         this.layout = layout;
53910         layout.on("regionresized", this.onRegionResized, this);
53911         layout.on("regioncollapsed", this.onRegionCollapsed, this);
53912         layout.on("regionexpanded", this.onRegionExpanded, this);
53913     },
53914     
53915     storeState : function(){
53916         this.provider.set(this.layout.id+"-layout-state", this.state);
53917     },
53918     
53919     onRegionResized : function(region, newSize){
53920         this.state[region.getPosition()].size = newSize;
53921         this.storeState();
53922     },
53923     
53924     onRegionCollapsed : function(region){
53925         this.state[region.getPosition()].collapsed = true;
53926         this.storeState();
53927     },
53928     
53929     onRegionExpanded : function(region){
53930         this.state[region.getPosition()].collapsed = false;
53931         this.storeState();
53932     }
53933 };/*
53934  * Based on:
53935  * Ext JS Library 1.1.1
53936  * Copyright(c) 2006-2007, Ext JS, LLC.
53937  *
53938  * Originally Released Under LGPL - original licence link has changed is not relivant.
53939  *
53940  * Fork - LGPL
53941  * <script type="text/javascript">
53942  */
53943 /**
53944  * @class Roo.ContentPanel
53945  * @extends Roo.util.Observable
53946  * A basic ContentPanel element.
53947  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
53948  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
53949  * @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
53950  * @cfg {Boolean}   closable      True if the panel can be closed/removed
53951  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
53952  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
53953  * @cfg {Toolbar}   toolbar       A toolbar for this panel
53954  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
53955  * @cfg {String} title          The title for this panel
53956  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
53957  * @cfg {String} url            Calls {@link #setUrl} with this value
53958  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
53959  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
53960  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
53961  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
53962
53963  * @constructor
53964  * Create a new ContentPanel.
53965  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
53966  * @param {String/Object} config A string to set only the title or a config object
53967  * @param {String} content (optional) Set the HTML content for this panel
53968  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
53969  */
53970 Roo.ContentPanel = function(el, config, content){
53971     
53972      
53973     /*
53974     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
53975         config = el;
53976         el = Roo.id();
53977     }
53978     if (config && config.parentLayout) { 
53979         el = config.parentLayout.el.createChild(); 
53980     }
53981     */
53982     if(el.autoCreate){ // xtype is available if this is called from factory
53983         config = el;
53984         el = Roo.id();
53985     }
53986     this.el = Roo.get(el);
53987     if(!this.el && config && config.autoCreate){
53988         if(typeof config.autoCreate == "object"){
53989             if(!config.autoCreate.id){
53990                 config.autoCreate.id = config.id||el;
53991             }
53992             this.el = Roo.DomHelper.append(document.body,
53993                         config.autoCreate, true);
53994         }else{
53995             this.el = Roo.DomHelper.append(document.body,
53996                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
53997         }
53998     }
53999     this.closable = false;
54000     this.loaded = false;
54001     this.active = false;
54002     if(typeof config == "string"){
54003         this.title = config;
54004     }else{
54005         Roo.apply(this, config);
54006     }
54007     
54008     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54009         this.wrapEl = this.el.wrap();
54010         this.toolbar.container = this.el.insertSibling(false, 'before');
54011         this.toolbar = new Roo.Toolbar(this.toolbar);
54012     }
54013     
54014     // xtype created footer. - not sure if will work as we normally have to render first..
54015     if (this.footer && !this.footer.el && this.footer.xtype) {
54016         if (!this.wrapEl) {
54017             this.wrapEl = this.el.wrap();
54018         }
54019     
54020         this.footer.container = this.wrapEl.createChild();
54021          
54022         this.footer = Roo.factory(this.footer, Roo);
54023         
54024     }
54025     
54026     if(this.resizeEl){
54027         this.resizeEl = Roo.get(this.resizeEl, true);
54028     }else{
54029         this.resizeEl = this.el;
54030     }
54031     // handle view.xtype
54032     
54033  
54034     
54035     
54036     this.addEvents({
54037         /**
54038          * @event activate
54039          * Fires when this panel is activated. 
54040          * @param {Roo.ContentPanel} this
54041          */
54042         "activate" : true,
54043         /**
54044          * @event deactivate
54045          * Fires when this panel is activated. 
54046          * @param {Roo.ContentPanel} this
54047          */
54048         "deactivate" : true,
54049
54050         /**
54051          * @event resize
54052          * Fires when this panel is resized if fitToFrame is true.
54053          * @param {Roo.ContentPanel} this
54054          * @param {Number} width The width after any component adjustments
54055          * @param {Number} height The height after any component adjustments
54056          */
54057         "resize" : true,
54058         
54059          /**
54060          * @event render
54061          * Fires when this tab is created
54062          * @param {Roo.ContentPanel} this
54063          */
54064         "render" : true
54065          
54066         
54067     });
54068     
54069
54070     
54071     
54072     if(this.autoScroll){
54073         this.resizeEl.setStyle("overflow", "auto");
54074     } else {
54075         // fix randome scrolling
54076         this.el.on('scroll', function() {
54077             Roo.log('fix random scolling');
54078             this.scrollTo('top',0); 
54079         });
54080     }
54081     content = content || this.content;
54082     if(content){
54083         this.setContent(content);
54084     }
54085     if(config && config.url){
54086         this.setUrl(this.url, this.params, this.loadOnce);
54087     }
54088     
54089     
54090     
54091     Roo.ContentPanel.superclass.constructor.call(this);
54092     
54093     if (this.view && typeof(this.view.xtype) != 'undefined') {
54094         this.view.el = this.el.appendChild(document.createElement("div"));
54095         this.view = Roo.factory(this.view); 
54096         this.view.render  &&  this.view.render(false, '');  
54097     }
54098     
54099     
54100     this.fireEvent('render', this);
54101 };
54102
54103 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54104     tabTip:'',
54105     setRegion : function(region){
54106         this.region = region;
54107         if(region){
54108            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54109         }else{
54110            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54111         } 
54112     },
54113     
54114     /**
54115      * Returns the toolbar for this Panel if one was configured. 
54116      * @return {Roo.Toolbar} 
54117      */
54118     getToolbar : function(){
54119         return this.toolbar;
54120     },
54121     
54122     setActiveState : function(active){
54123         this.active = active;
54124         if(!active){
54125             this.fireEvent("deactivate", this);
54126         }else{
54127             this.fireEvent("activate", this);
54128         }
54129     },
54130     /**
54131      * Updates this panel's element
54132      * @param {String} content The new content
54133      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54134     */
54135     setContent : function(content, loadScripts){
54136         this.el.update(content, loadScripts);
54137     },
54138
54139     ignoreResize : function(w, h){
54140         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54141             return true;
54142         }else{
54143             this.lastSize = {width: w, height: h};
54144             return false;
54145         }
54146     },
54147     /**
54148      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54149      * @return {Roo.UpdateManager} The UpdateManager
54150      */
54151     getUpdateManager : function(){
54152         return this.el.getUpdateManager();
54153     },
54154      /**
54155      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54156      * @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:
54157 <pre><code>
54158 panel.load({
54159     url: "your-url.php",
54160     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54161     callback: yourFunction,
54162     scope: yourObject, //(optional scope)
54163     discardUrl: false,
54164     nocache: false,
54165     text: "Loading...",
54166     timeout: 30,
54167     scripts: false
54168 });
54169 </code></pre>
54170      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54171      * 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.
54172      * @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}
54173      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54174      * @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.
54175      * @return {Roo.ContentPanel} this
54176      */
54177     load : function(){
54178         var um = this.el.getUpdateManager();
54179         um.update.apply(um, arguments);
54180         return this;
54181     },
54182
54183
54184     /**
54185      * 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.
54186      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54187      * @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)
54188      * @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)
54189      * @return {Roo.UpdateManager} The UpdateManager
54190      */
54191     setUrl : function(url, params, loadOnce){
54192         if(this.refreshDelegate){
54193             this.removeListener("activate", this.refreshDelegate);
54194         }
54195         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54196         this.on("activate", this.refreshDelegate);
54197         return this.el.getUpdateManager();
54198     },
54199     
54200     _handleRefresh : function(url, params, loadOnce){
54201         if(!loadOnce || !this.loaded){
54202             var updater = this.el.getUpdateManager();
54203             updater.update(url, params, this._setLoaded.createDelegate(this));
54204         }
54205     },
54206     
54207     _setLoaded : function(){
54208         this.loaded = true;
54209     }, 
54210     
54211     /**
54212      * Returns this panel's id
54213      * @return {String} 
54214      */
54215     getId : function(){
54216         return this.el.id;
54217     },
54218     
54219     /** 
54220      * Returns this panel's element - used by regiosn to add.
54221      * @return {Roo.Element} 
54222      */
54223     getEl : function(){
54224         return this.wrapEl || this.el;
54225     },
54226     
54227     adjustForComponents : function(width, height)
54228     {
54229         //Roo.log('adjustForComponents ');
54230         if(this.resizeEl != this.el){
54231             width -= this.el.getFrameWidth('lr');
54232             height -= this.el.getFrameWidth('tb');
54233         }
54234         if(this.toolbar){
54235             var te = this.toolbar.getEl();
54236             height -= te.getHeight();
54237             te.setWidth(width);
54238         }
54239         if(this.footer){
54240             var te = this.footer.getEl();
54241             //Roo.log("footer:" + te.getHeight());
54242             
54243             height -= te.getHeight();
54244             te.setWidth(width);
54245         }
54246         
54247         
54248         if(this.adjustments){
54249             width += this.adjustments[0];
54250             height += this.adjustments[1];
54251         }
54252         return {"width": width, "height": height};
54253     },
54254     
54255     setSize : function(width, height){
54256         if(this.fitToFrame && !this.ignoreResize(width, height)){
54257             if(this.fitContainer && this.resizeEl != this.el){
54258                 this.el.setSize(width, height);
54259             }
54260             var size = this.adjustForComponents(width, height);
54261             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54262             this.fireEvent('resize', this, size.width, size.height);
54263         }
54264     },
54265     
54266     /**
54267      * Returns this panel's title
54268      * @return {String} 
54269      */
54270     getTitle : function(){
54271         return this.title;
54272     },
54273     
54274     /**
54275      * Set this panel's title
54276      * @param {String} title
54277      */
54278     setTitle : function(title){
54279         this.title = title;
54280         if(this.region){
54281             this.region.updatePanelTitle(this, title);
54282         }
54283     },
54284     
54285     /**
54286      * Returns true is this panel was configured to be closable
54287      * @return {Boolean} 
54288      */
54289     isClosable : function(){
54290         return this.closable;
54291     },
54292     
54293     beforeSlide : function(){
54294         this.el.clip();
54295         this.resizeEl.clip();
54296     },
54297     
54298     afterSlide : function(){
54299         this.el.unclip();
54300         this.resizeEl.unclip();
54301     },
54302     
54303     /**
54304      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54305      *   Will fail silently if the {@link #setUrl} method has not been called.
54306      *   This does not activate the panel, just updates its content.
54307      */
54308     refresh : function(){
54309         if(this.refreshDelegate){
54310            this.loaded = false;
54311            this.refreshDelegate();
54312         }
54313     },
54314     
54315     /**
54316      * Destroys this panel
54317      */
54318     destroy : function(){
54319         this.el.removeAllListeners();
54320         var tempEl = document.createElement("span");
54321         tempEl.appendChild(this.el.dom);
54322         tempEl.innerHTML = "";
54323         this.el.remove();
54324         this.el = null;
54325     },
54326     
54327     /**
54328      * form - if the content panel contains a form - this is a reference to it.
54329      * @type {Roo.form.Form}
54330      */
54331     form : false,
54332     /**
54333      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54334      *    This contains a reference to it.
54335      * @type {Roo.View}
54336      */
54337     view : false,
54338     
54339       /**
54340      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54341      * <pre><code>
54342
54343 layout.addxtype({
54344        xtype : 'Form',
54345        items: [ .... ]
54346    }
54347 );
54348
54349 </code></pre>
54350      * @param {Object} cfg Xtype definition of item to add.
54351      */
54352     
54353     addxtype : function(cfg) {
54354         // add form..
54355         if (cfg.xtype.match(/^Form$/)) {
54356             
54357             var el;
54358             //if (this.footer) {
54359             //    el = this.footer.container.insertSibling(false, 'before');
54360             //} else {
54361                 el = this.el.createChild();
54362             //}
54363
54364             this.form = new  Roo.form.Form(cfg);
54365             
54366             
54367             if ( this.form.allItems.length) {
54368                 this.form.render(el.dom);
54369             }
54370             return this.form;
54371         }
54372         // should only have one of theses..
54373         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54374             // views.. should not be just added - used named prop 'view''
54375             
54376             cfg.el = this.el.appendChild(document.createElement("div"));
54377             // factory?
54378             
54379             var ret = new Roo.factory(cfg);
54380              
54381              ret.render && ret.render(false, ''); // render blank..
54382             this.view = ret;
54383             return ret;
54384         }
54385         return false;
54386     }
54387 });
54388
54389 /**
54390  * @class Roo.GridPanel
54391  * @extends Roo.ContentPanel
54392  * @constructor
54393  * Create a new GridPanel.
54394  * @param {Roo.grid.Grid} grid The grid for this panel
54395  * @param {String/Object} config A string to set only the panel's title, or a config object
54396  */
54397 Roo.GridPanel = function(grid, config){
54398     
54399   
54400     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54401         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54402         
54403     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54404     
54405     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54406     
54407     if(this.toolbar){
54408         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54409     }
54410     // xtype created footer. - not sure if will work as we normally have to render first..
54411     if (this.footer && !this.footer.el && this.footer.xtype) {
54412         
54413         this.footer.container = this.grid.getView().getFooterPanel(true);
54414         this.footer.dataSource = this.grid.dataSource;
54415         this.footer = Roo.factory(this.footer, Roo);
54416         
54417     }
54418     
54419     grid.monitorWindowResize = false; // turn off autosizing
54420     grid.autoHeight = false;
54421     grid.autoWidth = false;
54422     this.grid = grid;
54423     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54424 };
54425
54426 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54427     getId : function(){
54428         return this.grid.id;
54429     },
54430     
54431     /**
54432      * Returns the grid for this panel
54433      * @return {Roo.grid.Grid} 
54434      */
54435     getGrid : function(){
54436         return this.grid;    
54437     },
54438     
54439     setSize : function(width, height){
54440         if(!this.ignoreResize(width, height)){
54441             var grid = this.grid;
54442             var size = this.adjustForComponents(width, height);
54443             grid.getGridEl().setSize(size.width, size.height);
54444             grid.autoSize();
54445         }
54446     },
54447     
54448     beforeSlide : function(){
54449         this.grid.getView().scroller.clip();
54450     },
54451     
54452     afterSlide : function(){
54453         this.grid.getView().scroller.unclip();
54454     },
54455     
54456     destroy : function(){
54457         this.grid.destroy();
54458         delete this.grid;
54459         Roo.GridPanel.superclass.destroy.call(this); 
54460     }
54461 });
54462
54463
54464 /**
54465  * @class Roo.NestedLayoutPanel
54466  * @extends Roo.ContentPanel
54467  * @constructor
54468  * Create a new NestedLayoutPanel.
54469  * 
54470  * 
54471  * @param {Roo.BorderLayout} layout The layout for this panel
54472  * @param {String/Object} config A string to set only the title or a config object
54473  */
54474 Roo.NestedLayoutPanel = function(layout, config)
54475 {
54476     // construct with only one argument..
54477     /* FIXME - implement nicer consturctors
54478     if (layout.layout) {
54479         config = layout;
54480         layout = config.layout;
54481         delete config.layout;
54482     }
54483     if (layout.xtype && !layout.getEl) {
54484         // then layout needs constructing..
54485         layout = Roo.factory(layout, Roo);
54486     }
54487     */
54488     
54489     
54490     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54491     
54492     layout.monitorWindowResize = false; // turn off autosizing
54493     this.layout = layout;
54494     this.layout.getEl().addClass("x-layout-nested-layout");
54495     
54496     
54497     
54498     
54499 };
54500
54501 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54502
54503     setSize : function(width, height){
54504         if(!this.ignoreResize(width, height)){
54505             var size = this.adjustForComponents(width, height);
54506             var el = this.layout.getEl();
54507             el.setSize(size.width, size.height);
54508             var touch = el.dom.offsetWidth;
54509             this.layout.layout();
54510             // ie requires a double layout on the first pass
54511             if(Roo.isIE && !this.initialized){
54512                 this.initialized = true;
54513                 this.layout.layout();
54514             }
54515         }
54516     },
54517     
54518     // activate all subpanels if not currently active..
54519     
54520     setActiveState : function(active){
54521         this.active = active;
54522         if(!active){
54523             this.fireEvent("deactivate", this);
54524             return;
54525         }
54526         
54527         this.fireEvent("activate", this);
54528         // not sure if this should happen before or after..
54529         if (!this.layout) {
54530             return; // should not happen..
54531         }
54532         var reg = false;
54533         for (var r in this.layout.regions) {
54534             reg = this.layout.getRegion(r);
54535             if (reg.getActivePanel()) {
54536                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54537                 reg.setActivePanel(reg.getActivePanel());
54538                 continue;
54539             }
54540             if (!reg.panels.length) {
54541                 continue;
54542             }
54543             reg.showPanel(reg.getPanel(0));
54544         }
54545         
54546         
54547         
54548         
54549     },
54550     
54551     /**
54552      * Returns the nested BorderLayout for this panel
54553      * @return {Roo.BorderLayout} 
54554      */
54555     getLayout : function(){
54556         return this.layout;
54557     },
54558     
54559      /**
54560      * Adds a xtype elements to the layout of the nested panel
54561      * <pre><code>
54562
54563 panel.addxtype({
54564        xtype : 'ContentPanel',
54565        region: 'west',
54566        items: [ .... ]
54567    }
54568 );
54569
54570 panel.addxtype({
54571         xtype : 'NestedLayoutPanel',
54572         region: 'west',
54573         layout: {
54574            center: { },
54575            west: { }   
54576         },
54577         items : [ ... list of content panels or nested layout panels.. ]
54578    }
54579 );
54580 </code></pre>
54581      * @param {Object} cfg Xtype definition of item to add.
54582      */
54583     addxtype : function(cfg) {
54584         return this.layout.addxtype(cfg);
54585     
54586     }
54587 });
54588
54589 Roo.ScrollPanel = function(el, config, content){
54590     config = config || {};
54591     config.fitToFrame = true;
54592     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54593     
54594     this.el.dom.style.overflow = "hidden";
54595     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54596     this.el.removeClass("x-layout-inactive-content");
54597     this.el.on("mousewheel", this.onWheel, this);
54598
54599     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54600     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54601     up.unselectable(); down.unselectable();
54602     up.on("click", this.scrollUp, this);
54603     down.on("click", this.scrollDown, this);
54604     up.addClassOnOver("x-scroller-btn-over");
54605     down.addClassOnOver("x-scroller-btn-over");
54606     up.addClassOnClick("x-scroller-btn-click");
54607     down.addClassOnClick("x-scroller-btn-click");
54608     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54609
54610     this.resizeEl = this.el;
54611     this.el = wrap; this.up = up; this.down = down;
54612 };
54613
54614 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54615     increment : 100,
54616     wheelIncrement : 5,
54617     scrollUp : function(){
54618         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54619     },
54620
54621     scrollDown : function(){
54622         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54623     },
54624
54625     afterScroll : function(){
54626         var el = this.resizeEl;
54627         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54628         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54629         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54630     },
54631
54632     setSize : function(){
54633         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54634         this.afterScroll();
54635     },
54636
54637     onWheel : function(e){
54638         var d = e.getWheelDelta();
54639         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54640         this.afterScroll();
54641         e.stopEvent();
54642     },
54643
54644     setContent : function(content, loadScripts){
54645         this.resizeEl.update(content, loadScripts);
54646     }
54647
54648 });
54649
54650
54651
54652
54653
54654
54655
54656
54657
54658 /**
54659  * @class Roo.TreePanel
54660  * @extends Roo.ContentPanel
54661  * @constructor
54662  * Create a new TreePanel. - defaults to fit/scoll contents.
54663  * @param {String/Object} config A string to set only the panel's title, or a config object
54664  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
54665  */
54666 Roo.TreePanel = function(config){
54667     var el = config.el;
54668     var tree = config.tree;
54669     delete config.tree; 
54670     delete config.el; // hopefull!
54671     
54672     // wrapper for IE7 strict & safari scroll issue
54673     
54674     var treeEl = el.createChild();
54675     config.resizeEl = treeEl;
54676     
54677     
54678     
54679     Roo.TreePanel.superclass.constructor.call(this, el, config);
54680  
54681  
54682     this.tree = new Roo.tree.TreePanel(treeEl , tree);
54683     //console.log(tree);
54684     this.on('activate', function()
54685     {
54686         if (this.tree.rendered) {
54687             return;
54688         }
54689         //console.log('render tree');
54690         this.tree.render();
54691     });
54692     // this should not be needed.. - it's actually the 'el' that resizes?
54693     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
54694     
54695     //this.on('resize',  function (cp, w, h) {
54696     //        this.tree.innerCt.setWidth(w);
54697     //        this.tree.innerCt.setHeight(h);
54698     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
54699     //});
54700
54701         
54702     
54703 };
54704
54705 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
54706     fitToFrame : true,
54707     autoScroll : true
54708 });
54709
54710
54711
54712
54713
54714
54715
54716
54717
54718
54719
54720 /*
54721  * Based on:
54722  * Ext JS Library 1.1.1
54723  * Copyright(c) 2006-2007, Ext JS, LLC.
54724  *
54725  * Originally Released Under LGPL - original licence link has changed is not relivant.
54726  *
54727  * Fork - LGPL
54728  * <script type="text/javascript">
54729  */
54730  
54731
54732 /**
54733  * @class Roo.ReaderLayout
54734  * @extends Roo.BorderLayout
54735  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
54736  * center region containing two nested regions (a top one for a list view and one for item preview below),
54737  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
54738  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
54739  * expedites the setup of the overall layout and regions for this common application style.
54740  * Example:
54741  <pre><code>
54742 var reader = new Roo.ReaderLayout();
54743 var CP = Roo.ContentPanel;  // shortcut for adding
54744
54745 reader.beginUpdate();
54746 reader.add("north", new CP("north", "North"));
54747 reader.add("west", new CP("west", {title: "West"}));
54748 reader.add("east", new CP("east", {title: "East"}));
54749
54750 reader.regions.listView.add(new CP("listView", "List"));
54751 reader.regions.preview.add(new CP("preview", "Preview"));
54752 reader.endUpdate();
54753 </code></pre>
54754 * @constructor
54755 * Create a new ReaderLayout
54756 * @param {Object} config Configuration options
54757 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
54758 * document.body if omitted)
54759 */
54760 Roo.ReaderLayout = function(config, renderTo){
54761     var c = config || {size:{}};
54762     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
54763         north: c.north !== false ? Roo.apply({
54764             split:false,
54765             initialSize: 32,
54766             titlebar: false
54767         }, c.north) : false,
54768         west: c.west !== false ? Roo.apply({
54769             split:true,
54770             initialSize: 200,
54771             minSize: 175,
54772             maxSize: 400,
54773             titlebar: true,
54774             collapsible: true,
54775             animate: true,
54776             margins:{left:5,right:0,bottom:5,top:5},
54777             cmargins:{left:5,right:5,bottom:5,top:5}
54778         }, c.west) : false,
54779         east: c.east !== false ? Roo.apply({
54780             split:true,
54781             initialSize: 200,
54782             minSize: 175,
54783             maxSize: 400,
54784             titlebar: true,
54785             collapsible: true,
54786             animate: true,
54787             margins:{left:0,right:5,bottom:5,top:5},
54788             cmargins:{left:5,right:5,bottom:5,top:5}
54789         }, c.east) : false,
54790         center: Roo.apply({
54791             tabPosition: 'top',
54792             autoScroll:false,
54793             closeOnTab: true,
54794             titlebar:false,
54795             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
54796         }, c.center)
54797     });
54798
54799     this.el.addClass('x-reader');
54800
54801     this.beginUpdate();
54802
54803     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
54804         south: c.preview !== false ? Roo.apply({
54805             split:true,
54806             initialSize: 200,
54807             minSize: 100,
54808             autoScroll:true,
54809             collapsible:true,
54810             titlebar: true,
54811             cmargins:{top:5,left:0, right:0, bottom:0}
54812         }, c.preview) : false,
54813         center: Roo.apply({
54814             autoScroll:false,
54815             titlebar:false,
54816             minHeight:200
54817         }, c.listView)
54818     });
54819     this.add('center', new Roo.NestedLayoutPanel(inner,
54820             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
54821
54822     this.endUpdate();
54823
54824     this.regions.preview = inner.getRegion('south');
54825     this.regions.listView = inner.getRegion('center');
54826 };
54827
54828 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
54829  * Based on:
54830  * Ext JS Library 1.1.1
54831  * Copyright(c) 2006-2007, Ext JS, LLC.
54832  *
54833  * Originally Released Under LGPL - original licence link has changed is not relivant.
54834  *
54835  * Fork - LGPL
54836  * <script type="text/javascript">
54837  */
54838  
54839 /**
54840  * @class Roo.grid.Grid
54841  * @extends Roo.util.Observable
54842  * This class represents the primary interface of a component based grid control.
54843  * <br><br>Usage:<pre><code>
54844  var grid = new Roo.grid.Grid("my-container-id", {
54845      ds: myDataStore,
54846      cm: myColModel,
54847      selModel: mySelectionModel,
54848      autoSizeColumns: true,
54849      monitorWindowResize: false,
54850      trackMouseOver: true
54851  });
54852  // set any options
54853  grid.render();
54854  * </code></pre>
54855  * <b>Common Problems:</b><br/>
54856  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
54857  * element will correct this<br/>
54858  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
54859  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
54860  * are unpredictable.<br/>
54861  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
54862  * grid to calculate dimensions/offsets.<br/>
54863   * @constructor
54864  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
54865  * The container MUST have some type of size defined for the grid to fill. The container will be
54866  * automatically set to position relative if it isn't already.
54867  * @param {Object} config A config object that sets properties on this grid.
54868  */
54869 Roo.grid.Grid = function(container, config){
54870         // initialize the container
54871         this.container = Roo.get(container);
54872         this.container.update("");
54873         this.container.setStyle("overflow", "hidden");
54874     this.container.addClass('x-grid-container');
54875
54876     this.id = this.container.id;
54877
54878     Roo.apply(this, config);
54879     // check and correct shorthanded configs
54880     if(this.ds){
54881         this.dataSource = this.ds;
54882         delete this.ds;
54883     }
54884     if(this.cm){
54885         this.colModel = this.cm;
54886         delete this.cm;
54887     }
54888     if(this.sm){
54889         this.selModel = this.sm;
54890         delete this.sm;
54891     }
54892
54893     if (this.selModel) {
54894         this.selModel = Roo.factory(this.selModel, Roo.grid);
54895         this.sm = this.selModel;
54896         this.sm.xmodule = this.xmodule || false;
54897     }
54898     if (typeof(this.colModel.config) == 'undefined') {
54899         this.colModel = new Roo.grid.ColumnModel(this.colModel);
54900         this.cm = this.colModel;
54901         this.cm.xmodule = this.xmodule || false;
54902     }
54903     if (this.dataSource) {
54904         this.dataSource= Roo.factory(this.dataSource, Roo.data);
54905         this.ds = this.dataSource;
54906         this.ds.xmodule = this.xmodule || false;
54907          
54908     }
54909     
54910     
54911     
54912     if(this.width){
54913         this.container.setWidth(this.width);
54914     }
54915
54916     if(this.height){
54917         this.container.setHeight(this.height);
54918     }
54919     /** @private */
54920         this.addEvents({
54921         // raw events
54922         /**
54923          * @event click
54924          * The raw click event for the entire grid.
54925          * @param {Roo.EventObject} e
54926          */
54927         "click" : true,
54928         /**
54929          * @event dblclick
54930          * The raw dblclick event for the entire grid.
54931          * @param {Roo.EventObject} e
54932          */
54933         "dblclick" : true,
54934         /**
54935          * @event contextmenu
54936          * The raw contextmenu event for the entire grid.
54937          * @param {Roo.EventObject} e
54938          */
54939         "contextmenu" : true,
54940         /**
54941          * @event mousedown
54942          * The raw mousedown event for the entire grid.
54943          * @param {Roo.EventObject} e
54944          */
54945         "mousedown" : true,
54946         /**
54947          * @event mouseup
54948          * The raw mouseup event for the entire grid.
54949          * @param {Roo.EventObject} e
54950          */
54951         "mouseup" : true,
54952         /**
54953          * @event mouseover
54954          * The raw mouseover event for the entire grid.
54955          * @param {Roo.EventObject} e
54956          */
54957         "mouseover" : true,
54958         /**
54959          * @event mouseout
54960          * The raw mouseout event for the entire grid.
54961          * @param {Roo.EventObject} e
54962          */
54963         "mouseout" : true,
54964         /**
54965          * @event keypress
54966          * The raw keypress event for the entire grid.
54967          * @param {Roo.EventObject} e
54968          */
54969         "keypress" : true,
54970         /**
54971          * @event keydown
54972          * The raw keydown event for the entire grid.
54973          * @param {Roo.EventObject} e
54974          */
54975         "keydown" : true,
54976
54977         // custom events
54978
54979         /**
54980          * @event cellclick
54981          * Fires when a cell is clicked
54982          * @param {Grid} this
54983          * @param {Number} rowIndex
54984          * @param {Number} columnIndex
54985          * @param {Roo.EventObject} e
54986          */
54987         "cellclick" : true,
54988         /**
54989          * @event celldblclick
54990          * Fires when a cell is double clicked
54991          * @param {Grid} this
54992          * @param {Number} rowIndex
54993          * @param {Number} columnIndex
54994          * @param {Roo.EventObject} e
54995          */
54996         "celldblclick" : true,
54997         /**
54998          * @event rowclick
54999          * Fires when a row is clicked
55000          * @param {Grid} this
55001          * @param {Number} rowIndex
55002          * @param {Roo.EventObject} e
55003          */
55004         "rowclick" : true,
55005         /**
55006          * @event rowdblclick
55007          * Fires when a row is double clicked
55008          * @param {Grid} this
55009          * @param {Number} rowIndex
55010          * @param {Roo.EventObject} e
55011          */
55012         "rowdblclick" : true,
55013         /**
55014          * @event headerclick
55015          * Fires when a header is clicked
55016          * @param {Grid} this
55017          * @param {Number} columnIndex
55018          * @param {Roo.EventObject} e
55019          */
55020         "headerclick" : true,
55021         /**
55022          * @event headerdblclick
55023          * Fires when a header cell is double clicked
55024          * @param {Grid} this
55025          * @param {Number} columnIndex
55026          * @param {Roo.EventObject} e
55027          */
55028         "headerdblclick" : true,
55029         /**
55030          * @event rowcontextmenu
55031          * Fires when a row is right clicked
55032          * @param {Grid} this
55033          * @param {Number} rowIndex
55034          * @param {Roo.EventObject} e
55035          */
55036         "rowcontextmenu" : true,
55037         /**
55038          * @event cellcontextmenu
55039          * Fires when a cell is right clicked
55040          * @param {Grid} this
55041          * @param {Number} rowIndex
55042          * @param {Number} cellIndex
55043          * @param {Roo.EventObject} e
55044          */
55045          "cellcontextmenu" : true,
55046         /**
55047          * @event headercontextmenu
55048          * Fires when a header is right clicked
55049          * @param {Grid} this
55050          * @param {Number} columnIndex
55051          * @param {Roo.EventObject} e
55052          */
55053         "headercontextmenu" : true,
55054         /**
55055          * @event bodyscroll
55056          * Fires when the body element is scrolled
55057          * @param {Number} scrollLeft
55058          * @param {Number} scrollTop
55059          */
55060         "bodyscroll" : true,
55061         /**
55062          * @event columnresize
55063          * Fires when the user resizes a column
55064          * @param {Number} columnIndex
55065          * @param {Number} newSize
55066          */
55067         "columnresize" : true,
55068         /**
55069          * @event columnmove
55070          * Fires when the user moves a column
55071          * @param {Number} oldIndex
55072          * @param {Number} newIndex
55073          */
55074         "columnmove" : true,
55075         /**
55076          * @event startdrag
55077          * Fires when row(s) start being dragged
55078          * @param {Grid} this
55079          * @param {Roo.GridDD} dd The drag drop object
55080          * @param {event} e The raw browser event
55081          */
55082         "startdrag" : true,
55083         /**
55084          * @event enddrag
55085          * Fires when a drag operation is complete
55086          * @param {Grid} this
55087          * @param {Roo.GridDD} dd The drag drop object
55088          * @param {event} e The raw browser event
55089          */
55090         "enddrag" : true,
55091         /**
55092          * @event dragdrop
55093          * Fires when dragged row(s) are dropped on a valid DD target
55094          * @param {Grid} this
55095          * @param {Roo.GridDD} dd The drag drop object
55096          * @param {String} targetId The target drag drop object
55097          * @param {event} e The raw browser event
55098          */
55099         "dragdrop" : true,
55100         /**
55101          * @event dragover
55102          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55103          * @param {Grid} this
55104          * @param {Roo.GridDD} dd The drag drop object
55105          * @param {String} targetId The target drag drop object
55106          * @param {event} e The raw browser event
55107          */
55108         "dragover" : true,
55109         /**
55110          * @event dragenter
55111          *  Fires when the dragged row(s) first cross another DD target while being dragged
55112          * @param {Grid} this
55113          * @param {Roo.GridDD} dd The drag drop object
55114          * @param {String} targetId The target drag drop object
55115          * @param {event} e The raw browser event
55116          */
55117         "dragenter" : true,
55118         /**
55119          * @event dragout
55120          * Fires when the dragged row(s) leave another DD target while being dragged
55121          * @param {Grid} this
55122          * @param {Roo.GridDD} dd The drag drop object
55123          * @param {String} targetId The target drag drop object
55124          * @param {event} e The raw browser event
55125          */
55126         "dragout" : true,
55127         /**
55128          * @event rowclass
55129          * Fires when a row is rendered, so you can change add a style to it.
55130          * @param {GridView} gridview   The grid view
55131          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55132          */
55133         'rowclass' : true,
55134
55135         /**
55136          * @event render
55137          * Fires when the grid is rendered
55138          * @param {Grid} grid
55139          */
55140         'render' : true
55141     });
55142
55143     Roo.grid.Grid.superclass.constructor.call(this);
55144 };
55145 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55146     
55147     /**
55148      * @cfg {String} ddGroup - drag drop group.
55149      */
55150
55151     /**
55152      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55153      */
55154     minColumnWidth : 25,
55155
55156     /**
55157      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55158      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55159      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55160      */
55161     autoSizeColumns : false,
55162
55163     /**
55164      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55165      */
55166     autoSizeHeaders : true,
55167
55168     /**
55169      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55170      */
55171     monitorWindowResize : true,
55172
55173     /**
55174      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55175      * rows measured to get a columns size. Default is 0 (all rows).
55176      */
55177     maxRowsToMeasure : 0,
55178
55179     /**
55180      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55181      */
55182     trackMouseOver : true,
55183
55184     /**
55185     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55186     */
55187     
55188     /**
55189     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55190     */
55191     enableDragDrop : false,
55192     
55193     /**
55194     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55195     */
55196     enableColumnMove : true,
55197     
55198     /**
55199     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55200     */
55201     enableColumnHide : true,
55202     
55203     /**
55204     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55205     */
55206     enableRowHeightSync : false,
55207     
55208     /**
55209     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55210     */
55211     stripeRows : true,
55212     
55213     /**
55214     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55215     */
55216     autoHeight : false,
55217
55218     /**
55219      * @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.
55220      */
55221     autoExpandColumn : false,
55222
55223     /**
55224     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55225     * Default is 50.
55226     */
55227     autoExpandMin : 50,
55228
55229     /**
55230     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55231     */
55232     autoExpandMax : 1000,
55233
55234     /**
55235     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55236     */
55237     view : null,
55238
55239     /**
55240     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55241     */
55242     loadMask : false,
55243     /**
55244     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55245     */
55246     dropTarget: false,
55247     
55248    
55249     
55250     // private
55251     rendered : false,
55252
55253     /**
55254     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55255     * of a fixed width. Default is false.
55256     */
55257     /**
55258     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55259     */
55260     /**
55261      * Called once after all setup has been completed and the grid is ready to be rendered.
55262      * @return {Roo.grid.Grid} this
55263      */
55264     render : function()
55265     {
55266         var c = this.container;
55267         // try to detect autoHeight/width mode
55268         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55269             this.autoHeight = true;
55270         }
55271         var view = this.getView();
55272         view.init(this);
55273
55274         c.on("click", this.onClick, this);
55275         c.on("dblclick", this.onDblClick, this);
55276         c.on("contextmenu", this.onContextMenu, this);
55277         c.on("keydown", this.onKeyDown, this);
55278         if (Roo.isTouch) {
55279             c.on("touchstart", this.onTouchStart, this);
55280         }
55281
55282         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55283
55284         this.getSelectionModel().init(this);
55285
55286         view.render();
55287
55288         if(this.loadMask){
55289             this.loadMask = new Roo.LoadMask(this.container,
55290                     Roo.apply({store:this.dataSource}, this.loadMask));
55291         }
55292         
55293         
55294         if (this.toolbar && this.toolbar.xtype) {
55295             this.toolbar.container = this.getView().getHeaderPanel(true);
55296             this.toolbar = new Roo.Toolbar(this.toolbar);
55297         }
55298         if (this.footer && this.footer.xtype) {
55299             this.footer.dataSource = this.getDataSource();
55300             this.footer.container = this.getView().getFooterPanel(true);
55301             this.footer = Roo.factory(this.footer, Roo);
55302         }
55303         if (this.dropTarget && this.dropTarget.xtype) {
55304             delete this.dropTarget.xtype;
55305             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55306         }
55307         
55308         
55309         this.rendered = true;
55310         this.fireEvent('render', this);
55311         return this;
55312     },
55313
55314         /**
55315          * Reconfigures the grid to use a different Store and Column Model.
55316          * The View will be bound to the new objects and refreshed.
55317          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55318          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55319          */
55320     reconfigure : function(dataSource, colModel){
55321         if(this.loadMask){
55322             this.loadMask.destroy();
55323             this.loadMask = new Roo.LoadMask(this.container,
55324                     Roo.apply({store:dataSource}, this.loadMask));
55325         }
55326         this.view.bind(dataSource, colModel);
55327         this.dataSource = dataSource;
55328         this.colModel = colModel;
55329         this.view.refresh(true);
55330     },
55331
55332     // private
55333     onKeyDown : function(e){
55334         this.fireEvent("keydown", e);
55335     },
55336
55337     /**
55338      * Destroy this grid.
55339      * @param {Boolean} removeEl True to remove the element
55340      */
55341     destroy : function(removeEl, keepListeners){
55342         if(this.loadMask){
55343             this.loadMask.destroy();
55344         }
55345         var c = this.container;
55346         c.removeAllListeners();
55347         this.view.destroy();
55348         this.colModel.purgeListeners();
55349         if(!keepListeners){
55350             this.purgeListeners();
55351         }
55352         c.update("");
55353         if(removeEl === true){
55354             c.remove();
55355         }
55356     },
55357
55358     // private
55359     processEvent : function(name, e){
55360         // does this fire select???
55361         //Roo.log('grid:processEvent '  + name);
55362         
55363         if (name != 'touchstart' ) {
55364             this.fireEvent(name, e);    
55365         }
55366         
55367         var t = e.getTarget();
55368         var v = this.view;
55369         var header = v.findHeaderIndex(t);
55370         if(header !== false){
55371             var ename = name == 'touchstart' ? 'click' : name;
55372              
55373             this.fireEvent("header" + ename, this, header, e);
55374         }else{
55375             var row = v.findRowIndex(t);
55376             var cell = v.findCellIndex(t);
55377             if (name == 'touchstart') {
55378                 // first touch is always a click.
55379                 // hopefull this happens after selection is updated.?
55380                 name = false;
55381                 
55382                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55383                     var cs = this.selModel.getSelectedCell();
55384                     if (row == cs[0] && cell == cs[1]){
55385                         name = 'dblclick';
55386                     }
55387                 }
55388                 if (typeof(this.selModel.getSelections) != 'undefined') {
55389                     var cs = this.selModel.getSelections();
55390                     var ds = this.dataSource;
55391                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55392                         name = 'dblclick';
55393                     }
55394                 }
55395                 if (!name) {
55396                     return;
55397                 }
55398             }
55399             
55400             
55401             if(row !== false){
55402                 this.fireEvent("row" + name, this, row, e);
55403                 if(cell !== false){
55404                     this.fireEvent("cell" + name, this, row, cell, e);
55405                 }
55406             }
55407         }
55408     },
55409
55410     // private
55411     onClick : function(e){
55412         this.processEvent("click", e);
55413     },
55414    // private
55415     onTouchStart : function(e){
55416         this.processEvent("touchstart", e);
55417     },
55418
55419     // private
55420     onContextMenu : function(e, t){
55421         this.processEvent("contextmenu", e);
55422     },
55423
55424     // private
55425     onDblClick : function(e){
55426         this.processEvent("dblclick", e);
55427     },
55428
55429     // private
55430     walkCells : function(row, col, step, fn, scope){
55431         var cm = this.colModel, clen = cm.getColumnCount();
55432         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55433         if(step < 0){
55434             if(col < 0){
55435                 row--;
55436                 first = false;
55437             }
55438             while(row >= 0){
55439                 if(!first){
55440                     col = clen-1;
55441                 }
55442                 first = false;
55443                 while(col >= 0){
55444                     if(fn.call(scope || this, row, col, cm) === true){
55445                         return [row, col];
55446                     }
55447                     col--;
55448                 }
55449                 row--;
55450             }
55451         } else {
55452             if(col >= clen){
55453                 row++;
55454                 first = false;
55455             }
55456             while(row < rlen){
55457                 if(!first){
55458                     col = 0;
55459                 }
55460                 first = false;
55461                 while(col < clen){
55462                     if(fn.call(scope || this, row, col, cm) === true){
55463                         return [row, col];
55464                     }
55465                     col++;
55466                 }
55467                 row++;
55468             }
55469         }
55470         return null;
55471     },
55472
55473     // private
55474     getSelections : function(){
55475         return this.selModel.getSelections();
55476     },
55477
55478     /**
55479      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55480      * but if manual update is required this method will initiate it.
55481      */
55482     autoSize : function(){
55483         if(this.rendered){
55484             this.view.layout();
55485             if(this.view.adjustForScroll){
55486                 this.view.adjustForScroll();
55487             }
55488         }
55489     },
55490
55491     /**
55492      * Returns the grid's underlying element.
55493      * @return {Element} The element
55494      */
55495     getGridEl : function(){
55496         return this.container;
55497     },
55498
55499     // private for compatibility, overridden by editor grid
55500     stopEditing : function(){},
55501
55502     /**
55503      * Returns the grid's SelectionModel.
55504      * @return {SelectionModel}
55505      */
55506     getSelectionModel : function(){
55507         if(!this.selModel){
55508             this.selModel = new Roo.grid.RowSelectionModel();
55509         }
55510         return this.selModel;
55511     },
55512
55513     /**
55514      * Returns the grid's DataSource.
55515      * @return {DataSource}
55516      */
55517     getDataSource : function(){
55518         return this.dataSource;
55519     },
55520
55521     /**
55522      * Returns the grid's ColumnModel.
55523      * @return {ColumnModel}
55524      */
55525     getColumnModel : function(){
55526         return this.colModel;
55527     },
55528
55529     /**
55530      * Returns the grid's GridView object.
55531      * @return {GridView}
55532      */
55533     getView : function(){
55534         if(!this.view){
55535             this.view = new Roo.grid.GridView(this.viewConfig);
55536         }
55537         return this.view;
55538     },
55539     /**
55540      * Called to get grid's drag proxy text, by default returns this.ddText.
55541      * @return {String}
55542      */
55543     getDragDropText : function(){
55544         var count = this.selModel.getCount();
55545         return String.format(this.ddText, count, count == 1 ? '' : 's');
55546     }
55547 });
55548 /**
55549  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55550  * %0 is replaced with the number of selected rows.
55551  * @type String
55552  */
55553 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
55554  * Based on:
55555  * Ext JS Library 1.1.1
55556  * Copyright(c) 2006-2007, Ext JS, LLC.
55557  *
55558  * Originally Released Under LGPL - original licence link has changed is not relivant.
55559  *
55560  * Fork - LGPL
55561  * <script type="text/javascript">
55562  */
55563  
55564 Roo.grid.AbstractGridView = function(){
55565         this.grid = null;
55566         
55567         this.events = {
55568             "beforerowremoved" : true,
55569             "beforerowsinserted" : true,
55570             "beforerefresh" : true,
55571             "rowremoved" : true,
55572             "rowsinserted" : true,
55573             "rowupdated" : true,
55574             "refresh" : true
55575         };
55576     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55577 };
55578
55579 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55580     rowClass : "x-grid-row",
55581     cellClass : "x-grid-cell",
55582     tdClass : "x-grid-td",
55583     hdClass : "x-grid-hd",
55584     splitClass : "x-grid-hd-split",
55585     
55586     init: function(grid){
55587         this.grid = grid;
55588                 var cid = this.grid.getGridEl().id;
55589         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55590         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55591         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
55592         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
55593         },
55594         
55595     getColumnRenderers : function(){
55596         var renderers = [];
55597         var cm = this.grid.colModel;
55598         var colCount = cm.getColumnCount();
55599         for(var i = 0; i < colCount; i++){
55600             renderers[i] = cm.getRenderer(i);
55601         }
55602         return renderers;
55603     },
55604     
55605     getColumnIds : function(){
55606         var ids = [];
55607         var cm = this.grid.colModel;
55608         var colCount = cm.getColumnCount();
55609         for(var i = 0; i < colCount; i++){
55610             ids[i] = cm.getColumnId(i);
55611         }
55612         return ids;
55613     },
55614     
55615     getDataIndexes : function(){
55616         if(!this.indexMap){
55617             this.indexMap = this.buildIndexMap();
55618         }
55619         return this.indexMap.colToData;
55620     },
55621     
55622     getColumnIndexByDataIndex : function(dataIndex){
55623         if(!this.indexMap){
55624             this.indexMap = this.buildIndexMap();
55625         }
55626         return this.indexMap.dataToCol[dataIndex];
55627     },
55628     
55629     /**
55630      * Set a css style for a column dynamically. 
55631      * @param {Number} colIndex The index of the column
55632      * @param {String} name The css property name
55633      * @param {String} value The css value
55634      */
55635     setCSSStyle : function(colIndex, name, value){
55636         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
55637         Roo.util.CSS.updateRule(selector, name, value);
55638     },
55639     
55640     generateRules : function(cm){
55641         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
55642         Roo.util.CSS.removeStyleSheet(rulesId);
55643         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
55644             var cid = cm.getColumnId(i);
55645             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
55646                          this.tdSelector, cid, " {\n}\n",
55647                          this.hdSelector, cid, " {\n}\n",
55648                          this.splitSelector, cid, " {\n}\n");
55649         }
55650         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
55651     }
55652 });/*
55653  * Based on:
55654  * Ext JS Library 1.1.1
55655  * Copyright(c) 2006-2007, Ext JS, LLC.
55656  *
55657  * Originally Released Under LGPL - original licence link has changed is not relivant.
55658  *
55659  * Fork - LGPL
55660  * <script type="text/javascript">
55661  */
55662
55663 // private
55664 // This is a support class used internally by the Grid components
55665 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
55666     this.grid = grid;
55667     this.view = grid.getView();
55668     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55669     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
55670     if(hd2){
55671         this.setHandleElId(Roo.id(hd));
55672         this.setOuterHandleElId(Roo.id(hd2));
55673     }
55674     this.scroll = false;
55675 };
55676 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
55677     maxDragWidth: 120,
55678     getDragData : function(e){
55679         var t = Roo.lib.Event.getTarget(e);
55680         var h = this.view.findHeaderCell(t);
55681         if(h){
55682             return {ddel: h.firstChild, header:h};
55683         }
55684         return false;
55685     },
55686
55687     onInitDrag : function(e){
55688         this.view.headersDisabled = true;
55689         var clone = this.dragData.ddel.cloneNode(true);
55690         clone.id = Roo.id();
55691         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
55692         this.proxy.update(clone);
55693         return true;
55694     },
55695
55696     afterValidDrop : function(){
55697         var v = this.view;
55698         setTimeout(function(){
55699             v.headersDisabled = false;
55700         }, 50);
55701     },
55702
55703     afterInvalidDrop : function(){
55704         var v = this.view;
55705         setTimeout(function(){
55706             v.headersDisabled = false;
55707         }, 50);
55708     }
55709 });
55710 /*
55711  * Based on:
55712  * Ext JS Library 1.1.1
55713  * Copyright(c) 2006-2007, Ext JS, LLC.
55714  *
55715  * Originally Released Under LGPL - original licence link has changed is not relivant.
55716  *
55717  * Fork - LGPL
55718  * <script type="text/javascript">
55719  */
55720 // private
55721 // This is a support class used internally by the Grid components
55722 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
55723     this.grid = grid;
55724     this.view = grid.getView();
55725     // split the proxies so they don't interfere with mouse events
55726     this.proxyTop = Roo.DomHelper.append(document.body, {
55727         cls:"col-move-top", html:"&#160;"
55728     }, true);
55729     this.proxyBottom = Roo.DomHelper.append(document.body, {
55730         cls:"col-move-bottom", html:"&#160;"
55731     }, true);
55732     this.proxyTop.hide = this.proxyBottom.hide = function(){
55733         this.setLeftTop(-100,-100);
55734         this.setStyle("visibility", "hidden");
55735     };
55736     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
55737     // temporarily disabled
55738     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
55739     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
55740 };
55741 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
55742     proxyOffsets : [-4, -9],
55743     fly: Roo.Element.fly,
55744
55745     getTargetFromEvent : function(e){
55746         var t = Roo.lib.Event.getTarget(e);
55747         var cindex = this.view.findCellIndex(t);
55748         if(cindex !== false){
55749             return this.view.getHeaderCell(cindex);
55750         }
55751         return null;
55752     },
55753
55754     nextVisible : function(h){
55755         var v = this.view, cm = this.grid.colModel;
55756         h = h.nextSibling;
55757         while(h){
55758             if(!cm.isHidden(v.getCellIndex(h))){
55759                 return h;
55760             }
55761             h = h.nextSibling;
55762         }
55763         return null;
55764     },
55765
55766     prevVisible : function(h){
55767         var v = this.view, cm = this.grid.colModel;
55768         h = h.prevSibling;
55769         while(h){
55770             if(!cm.isHidden(v.getCellIndex(h))){
55771                 return h;
55772             }
55773             h = h.prevSibling;
55774         }
55775         return null;
55776     },
55777
55778     positionIndicator : function(h, n, e){
55779         var x = Roo.lib.Event.getPageX(e);
55780         var r = Roo.lib.Dom.getRegion(n.firstChild);
55781         var px, pt, py = r.top + this.proxyOffsets[1];
55782         if((r.right - x) <= (r.right-r.left)/2){
55783             px = r.right+this.view.borderWidth;
55784             pt = "after";
55785         }else{
55786             px = r.left;
55787             pt = "before";
55788         }
55789         var oldIndex = this.view.getCellIndex(h);
55790         var newIndex = this.view.getCellIndex(n);
55791
55792         if(this.grid.colModel.isFixed(newIndex)){
55793             return false;
55794         }
55795
55796         var locked = this.grid.colModel.isLocked(newIndex);
55797
55798         if(pt == "after"){
55799             newIndex++;
55800         }
55801         if(oldIndex < newIndex){
55802             newIndex--;
55803         }
55804         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
55805             return false;
55806         }
55807         px +=  this.proxyOffsets[0];
55808         this.proxyTop.setLeftTop(px, py);
55809         this.proxyTop.show();
55810         if(!this.bottomOffset){
55811             this.bottomOffset = this.view.mainHd.getHeight();
55812         }
55813         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
55814         this.proxyBottom.show();
55815         return pt;
55816     },
55817
55818     onNodeEnter : function(n, dd, e, data){
55819         if(data.header != n){
55820             this.positionIndicator(data.header, n, e);
55821         }
55822     },
55823
55824     onNodeOver : function(n, dd, e, data){
55825         var result = false;
55826         if(data.header != n){
55827             result = this.positionIndicator(data.header, n, e);
55828         }
55829         if(!result){
55830             this.proxyTop.hide();
55831             this.proxyBottom.hide();
55832         }
55833         return result ? this.dropAllowed : this.dropNotAllowed;
55834     },
55835
55836     onNodeOut : function(n, dd, e, data){
55837         this.proxyTop.hide();
55838         this.proxyBottom.hide();
55839     },
55840
55841     onNodeDrop : function(n, dd, e, data){
55842         var h = data.header;
55843         if(h != n){
55844             var cm = this.grid.colModel;
55845             var x = Roo.lib.Event.getPageX(e);
55846             var r = Roo.lib.Dom.getRegion(n.firstChild);
55847             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
55848             var oldIndex = this.view.getCellIndex(h);
55849             var newIndex = this.view.getCellIndex(n);
55850             var locked = cm.isLocked(newIndex);
55851             if(pt == "after"){
55852                 newIndex++;
55853             }
55854             if(oldIndex < newIndex){
55855                 newIndex--;
55856             }
55857             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
55858                 return false;
55859             }
55860             cm.setLocked(oldIndex, locked, true);
55861             cm.moveColumn(oldIndex, newIndex);
55862             this.grid.fireEvent("columnmove", oldIndex, newIndex);
55863             return true;
55864         }
55865         return false;
55866     }
55867 });
55868 /*
55869  * Based on:
55870  * Ext JS Library 1.1.1
55871  * Copyright(c) 2006-2007, Ext JS, LLC.
55872  *
55873  * Originally Released Under LGPL - original licence link has changed is not relivant.
55874  *
55875  * Fork - LGPL
55876  * <script type="text/javascript">
55877  */
55878   
55879 /**
55880  * @class Roo.grid.GridView
55881  * @extends Roo.util.Observable
55882  *
55883  * @constructor
55884  * @param {Object} config
55885  */
55886 Roo.grid.GridView = function(config){
55887     Roo.grid.GridView.superclass.constructor.call(this);
55888     this.el = null;
55889
55890     Roo.apply(this, config);
55891 };
55892
55893 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
55894
55895     unselectable :  'unselectable="on"',
55896     unselectableCls :  'x-unselectable',
55897     
55898     
55899     rowClass : "x-grid-row",
55900
55901     cellClass : "x-grid-col",
55902
55903     tdClass : "x-grid-td",
55904
55905     hdClass : "x-grid-hd",
55906
55907     splitClass : "x-grid-split",
55908
55909     sortClasses : ["sort-asc", "sort-desc"],
55910
55911     enableMoveAnim : false,
55912
55913     hlColor: "C3DAF9",
55914
55915     dh : Roo.DomHelper,
55916
55917     fly : Roo.Element.fly,
55918
55919     css : Roo.util.CSS,
55920
55921     borderWidth: 1,
55922
55923     splitOffset: 3,
55924
55925     scrollIncrement : 22,
55926
55927     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
55928
55929     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
55930
55931     bind : function(ds, cm){
55932         if(this.ds){
55933             this.ds.un("load", this.onLoad, this);
55934             this.ds.un("datachanged", this.onDataChange, this);
55935             this.ds.un("add", this.onAdd, this);
55936             this.ds.un("remove", this.onRemove, this);
55937             this.ds.un("update", this.onUpdate, this);
55938             this.ds.un("clear", this.onClear, this);
55939         }
55940         if(ds){
55941             ds.on("load", this.onLoad, this);
55942             ds.on("datachanged", this.onDataChange, this);
55943             ds.on("add", this.onAdd, this);
55944             ds.on("remove", this.onRemove, this);
55945             ds.on("update", this.onUpdate, this);
55946             ds.on("clear", this.onClear, this);
55947         }
55948         this.ds = ds;
55949
55950         if(this.cm){
55951             this.cm.un("widthchange", this.onColWidthChange, this);
55952             this.cm.un("headerchange", this.onHeaderChange, this);
55953             this.cm.un("hiddenchange", this.onHiddenChange, this);
55954             this.cm.un("columnmoved", this.onColumnMove, this);
55955             this.cm.un("columnlockchange", this.onColumnLock, this);
55956         }
55957         if(cm){
55958             this.generateRules(cm);
55959             cm.on("widthchange", this.onColWidthChange, this);
55960             cm.on("headerchange", this.onHeaderChange, this);
55961             cm.on("hiddenchange", this.onHiddenChange, this);
55962             cm.on("columnmoved", this.onColumnMove, this);
55963             cm.on("columnlockchange", this.onColumnLock, this);
55964         }
55965         this.cm = cm;
55966     },
55967
55968     init: function(grid){
55969         Roo.grid.GridView.superclass.init.call(this, grid);
55970
55971         this.bind(grid.dataSource, grid.colModel);
55972
55973         grid.on("headerclick", this.handleHeaderClick, this);
55974
55975         if(grid.trackMouseOver){
55976             grid.on("mouseover", this.onRowOver, this);
55977             grid.on("mouseout", this.onRowOut, this);
55978         }
55979         grid.cancelTextSelection = function(){};
55980         this.gridId = grid.id;
55981
55982         var tpls = this.templates || {};
55983
55984         if(!tpls.master){
55985             tpls.master = new Roo.Template(
55986                '<div class="x-grid" hidefocus="true">',
55987                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
55988                   '<div class="x-grid-topbar"></div>',
55989                   '<div class="x-grid-scroller"><div></div></div>',
55990                   '<div class="x-grid-locked">',
55991                       '<div class="x-grid-header">{lockedHeader}</div>',
55992                       '<div class="x-grid-body">{lockedBody}</div>',
55993                   "</div>",
55994                   '<div class="x-grid-viewport">',
55995                       '<div class="x-grid-header">{header}</div>',
55996                       '<div class="x-grid-body">{body}</div>',
55997                   "</div>",
55998                   '<div class="x-grid-bottombar"></div>',
55999                  
56000                   '<div class="x-grid-resize-proxy">&#160;</div>',
56001                "</div>"
56002             );
56003             tpls.master.disableformats = true;
56004         }
56005
56006         if(!tpls.header){
56007             tpls.header = new Roo.Template(
56008                '<table border="0" cellspacing="0" cellpadding="0">',
56009                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56010                "</table>{splits}"
56011             );
56012             tpls.header.disableformats = true;
56013         }
56014         tpls.header.compile();
56015
56016         if(!tpls.hcell){
56017             tpls.hcell = new Roo.Template(
56018                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56019                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56020                 "</div></td>"
56021              );
56022              tpls.hcell.disableFormats = true;
56023         }
56024         tpls.hcell.compile();
56025
56026         if(!tpls.hsplit){
56027             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56028                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56029             tpls.hsplit.disableFormats = true;
56030         }
56031         tpls.hsplit.compile();
56032
56033         if(!tpls.body){
56034             tpls.body = new Roo.Template(
56035                '<table border="0" cellspacing="0" cellpadding="0">',
56036                "<tbody>{rows}</tbody>",
56037                "</table>"
56038             );
56039             tpls.body.disableFormats = true;
56040         }
56041         tpls.body.compile();
56042
56043         if(!tpls.row){
56044             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56045             tpls.row.disableFormats = true;
56046         }
56047         tpls.row.compile();
56048
56049         if(!tpls.cell){
56050             tpls.cell = new Roo.Template(
56051                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56052                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56053                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56054                 "</td>"
56055             );
56056             tpls.cell.disableFormats = true;
56057         }
56058         tpls.cell.compile();
56059
56060         this.templates = tpls;
56061     },
56062
56063     // remap these for backwards compat
56064     onColWidthChange : function(){
56065         this.updateColumns.apply(this, arguments);
56066     },
56067     onHeaderChange : function(){
56068         this.updateHeaders.apply(this, arguments);
56069     }, 
56070     onHiddenChange : function(){
56071         this.handleHiddenChange.apply(this, arguments);
56072     },
56073     onColumnMove : function(){
56074         this.handleColumnMove.apply(this, arguments);
56075     },
56076     onColumnLock : function(){
56077         this.handleLockChange.apply(this, arguments);
56078     },
56079
56080     onDataChange : function(){
56081         this.refresh();
56082         this.updateHeaderSortState();
56083     },
56084
56085     onClear : function(){
56086         this.refresh();
56087     },
56088
56089     onUpdate : function(ds, record){
56090         this.refreshRow(record);
56091     },
56092
56093     refreshRow : function(record){
56094         var ds = this.ds, index;
56095         if(typeof record == 'number'){
56096             index = record;
56097             record = ds.getAt(index);
56098         }else{
56099             index = ds.indexOf(record);
56100         }
56101         this.insertRows(ds, index, index, true);
56102         this.onRemove(ds, record, index+1, true);
56103         this.syncRowHeights(index, index);
56104         this.layout();
56105         this.fireEvent("rowupdated", this, index, record);
56106     },
56107
56108     onAdd : function(ds, records, index){
56109         this.insertRows(ds, index, index + (records.length-1));
56110     },
56111
56112     onRemove : function(ds, record, index, isUpdate){
56113         if(isUpdate !== true){
56114             this.fireEvent("beforerowremoved", this, index, record);
56115         }
56116         var bt = this.getBodyTable(), lt = this.getLockedTable();
56117         if(bt.rows[index]){
56118             bt.firstChild.removeChild(bt.rows[index]);
56119         }
56120         if(lt.rows[index]){
56121             lt.firstChild.removeChild(lt.rows[index]);
56122         }
56123         if(isUpdate !== true){
56124             this.stripeRows(index);
56125             this.syncRowHeights(index, index);
56126             this.layout();
56127             this.fireEvent("rowremoved", this, index, record);
56128         }
56129     },
56130
56131     onLoad : function(){
56132         this.scrollToTop();
56133     },
56134
56135     /**
56136      * Scrolls the grid to the top
56137      */
56138     scrollToTop : function(){
56139         if(this.scroller){
56140             this.scroller.dom.scrollTop = 0;
56141             this.syncScroll();
56142         }
56143     },
56144
56145     /**
56146      * Gets a panel in the header of the grid that can be used for toolbars etc.
56147      * After modifying the contents of this panel a call to grid.autoSize() may be
56148      * required to register any changes in size.
56149      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56150      * @return Roo.Element
56151      */
56152     getHeaderPanel : function(doShow){
56153         if(doShow){
56154             this.headerPanel.show();
56155         }
56156         return this.headerPanel;
56157     },
56158
56159     /**
56160      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56161      * After modifying the contents of this panel a call to grid.autoSize() may be
56162      * required to register any changes in size.
56163      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56164      * @return Roo.Element
56165      */
56166     getFooterPanel : function(doShow){
56167         if(doShow){
56168             this.footerPanel.show();
56169         }
56170         return this.footerPanel;
56171     },
56172
56173     initElements : function(){
56174         var E = Roo.Element;
56175         var el = this.grid.getGridEl().dom.firstChild;
56176         var cs = el.childNodes;
56177
56178         this.el = new E(el);
56179         
56180          this.focusEl = new E(el.firstChild);
56181         this.focusEl.swallowEvent("click", true);
56182         
56183         this.headerPanel = new E(cs[1]);
56184         this.headerPanel.enableDisplayMode("block");
56185
56186         this.scroller = new E(cs[2]);
56187         this.scrollSizer = new E(this.scroller.dom.firstChild);
56188
56189         this.lockedWrap = new E(cs[3]);
56190         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56191         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56192
56193         this.mainWrap = new E(cs[4]);
56194         this.mainHd = new E(this.mainWrap.dom.firstChild);
56195         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56196
56197         this.footerPanel = new E(cs[5]);
56198         this.footerPanel.enableDisplayMode("block");
56199
56200         this.resizeProxy = new E(cs[6]);
56201
56202         this.headerSelector = String.format(
56203            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56204            this.lockedHd.id, this.mainHd.id
56205         );
56206
56207         this.splitterSelector = String.format(
56208            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56209            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56210         );
56211     },
56212     idToCssName : function(s)
56213     {
56214         return s.replace(/[^a-z0-9]+/ig, '-');
56215     },
56216
56217     getHeaderCell : function(index){
56218         return Roo.DomQuery.select(this.headerSelector)[index];
56219     },
56220
56221     getHeaderCellMeasure : function(index){
56222         return this.getHeaderCell(index).firstChild;
56223     },
56224
56225     getHeaderCellText : function(index){
56226         return this.getHeaderCell(index).firstChild.firstChild;
56227     },
56228
56229     getLockedTable : function(){
56230         return this.lockedBody.dom.firstChild;
56231     },
56232
56233     getBodyTable : function(){
56234         return this.mainBody.dom.firstChild;
56235     },
56236
56237     getLockedRow : function(index){
56238         return this.getLockedTable().rows[index];
56239     },
56240
56241     getRow : function(index){
56242         return this.getBodyTable().rows[index];
56243     },
56244
56245     getRowComposite : function(index){
56246         if(!this.rowEl){
56247             this.rowEl = new Roo.CompositeElementLite();
56248         }
56249         var els = [], lrow, mrow;
56250         if(lrow = this.getLockedRow(index)){
56251             els.push(lrow);
56252         }
56253         if(mrow = this.getRow(index)){
56254             els.push(mrow);
56255         }
56256         this.rowEl.elements = els;
56257         return this.rowEl;
56258     },
56259     /**
56260      * Gets the 'td' of the cell
56261      * 
56262      * @param {Integer} rowIndex row to select
56263      * @param {Integer} colIndex column to select
56264      * 
56265      * @return {Object} 
56266      */
56267     getCell : function(rowIndex, colIndex){
56268         var locked = this.cm.getLockedCount();
56269         var source;
56270         if(colIndex < locked){
56271             source = this.lockedBody.dom.firstChild;
56272         }else{
56273             source = this.mainBody.dom.firstChild;
56274             colIndex -= locked;
56275         }
56276         return source.rows[rowIndex].childNodes[colIndex];
56277     },
56278
56279     getCellText : function(rowIndex, colIndex){
56280         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56281     },
56282
56283     getCellBox : function(cell){
56284         var b = this.fly(cell).getBox();
56285         if(Roo.isOpera){ // opera fails to report the Y
56286             b.y = cell.offsetTop + this.mainBody.getY();
56287         }
56288         return b;
56289     },
56290
56291     getCellIndex : function(cell){
56292         var id = String(cell.className).match(this.cellRE);
56293         if(id){
56294             return parseInt(id[1], 10);
56295         }
56296         return 0;
56297     },
56298
56299     findHeaderIndex : function(n){
56300         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56301         return r ? this.getCellIndex(r) : false;
56302     },
56303
56304     findHeaderCell : function(n){
56305         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56306         return r ? r : false;
56307     },
56308
56309     findRowIndex : function(n){
56310         if(!n){
56311             return false;
56312         }
56313         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56314         return r ? r.rowIndex : false;
56315     },
56316
56317     findCellIndex : function(node){
56318         var stop = this.el.dom;
56319         while(node && node != stop){
56320             if(this.findRE.test(node.className)){
56321                 return this.getCellIndex(node);
56322             }
56323             node = node.parentNode;
56324         }
56325         return false;
56326     },
56327
56328     getColumnId : function(index){
56329         return this.cm.getColumnId(index);
56330     },
56331
56332     getSplitters : function()
56333     {
56334         if(this.splitterSelector){
56335            return Roo.DomQuery.select(this.splitterSelector);
56336         }else{
56337             return null;
56338       }
56339     },
56340
56341     getSplitter : function(index){
56342         return this.getSplitters()[index];
56343     },
56344
56345     onRowOver : function(e, t){
56346         var row;
56347         if((row = this.findRowIndex(t)) !== false){
56348             this.getRowComposite(row).addClass("x-grid-row-over");
56349         }
56350     },
56351
56352     onRowOut : function(e, t){
56353         var row;
56354         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56355             this.getRowComposite(row).removeClass("x-grid-row-over");
56356         }
56357     },
56358
56359     renderHeaders : function(){
56360         var cm = this.cm;
56361         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56362         var cb = [], lb = [], sb = [], lsb = [], p = {};
56363         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56364             p.cellId = "x-grid-hd-0-" + i;
56365             p.splitId = "x-grid-csplit-0-" + i;
56366             p.id = cm.getColumnId(i);
56367             p.value = cm.getColumnHeader(i) || "";
56368             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56369             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56370             if(!cm.isLocked(i)){
56371                 cb[cb.length] = ct.apply(p);
56372                 sb[sb.length] = st.apply(p);
56373             }else{
56374                 lb[lb.length] = ct.apply(p);
56375                 lsb[lsb.length] = st.apply(p);
56376             }
56377         }
56378         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56379                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56380     },
56381
56382     updateHeaders : function(){
56383         var html = this.renderHeaders();
56384         this.lockedHd.update(html[0]);
56385         this.mainHd.update(html[1]);
56386     },
56387
56388     /**
56389      * Focuses the specified row.
56390      * @param {Number} row The row index
56391      */
56392     focusRow : function(row)
56393     {
56394         //Roo.log('GridView.focusRow');
56395         var x = this.scroller.dom.scrollLeft;
56396         this.focusCell(row, 0, false);
56397         this.scroller.dom.scrollLeft = x;
56398     },
56399
56400     /**
56401      * Focuses the specified cell.
56402      * @param {Number} row The row index
56403      * @param {Number} col The column index
56404      * @param {Boolean} hscroll false to disable horizontal scrolling
56405      */
56406     focusCell : function(row, col, hscroll)
56407     {
56408         //Roo.log('GridView.focusCell');
56409         var el = this.ensureVisible(row, col, hscroll);
56410         this.focusEl.alignTo(el, "tl-tl");
56411         if(Roo.isGecko){
56412             this.focusEl.focus();
56413         }else{
56414             this.focusEl.focus.defer(1, this.focusEl);
56415         }
56416     },
56417
56418     /**
56419      * Scrolls the specified cell into view
56420      * @param {Number} row The row index
56421      * @param {Number} col The column index
56422      * @param {Boolean} hscroll false to disable horizontal scrolling
56423      */
56424     ensureVisible : function(row, col, hscroll)
56425     {
56426         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56427         //return null; //disable for testing.
56428         if(typeof row != "number"){
56429             row = row.rowIndex;
56430         }
56431         if(row < 0 && row >= this.ds.getCount()){
56432             return  null;
56433         }
56434         col = (col !== undefined ? col : 0);
56435         var cm = this.grid.colModel;
56436         while(cm.isHidden(col)){
56437             col++;
56438         }
56439
56440         var el = this.getCell(row, col);
56441         if(!el){
56442             return null;
56443         }
56444         var c = this.scroller.dom;
56445
56446         var ctop = parseInt(el.offsetTop, 10);
56447         var cleft = parseInt(el.offsetLeft, 10);
56448         var cbot = ctop + el.offsetHeight;
56449         var cright = cleft + el.offsetWidth;
56450         
56451         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56452         var stop = parseInt(c.scrollTop, 10);
56453         var sleft = parseInt(c.scrollLeft, 10);
56454         var sbot = stop + ch;
56455         var sright = sleft + c.clientWidth;
56456         /*
56457         Roo.log('GridView.ensureVisible:' +
56458                 ' ctop:' + ctop +
56459                 ' c.clientHeight:' + c.clientHeight +
56460                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56461                 ' stop:' + stop +
56462                 ' cbot:' + cbot +
56463                 ' sbot:' + sbot +
56464                 ' ch:' + ch  
56465                 );
56466         */
56467         if(ctop < stop){
56468              c.scrollTop = ctop;
56469             //Roo.log("set scrolltop to ctop DISABLE?");
56470         }else if(cbot > sbot){
56471             //Roo.log("set scrolltop to cbot-ch");
56472             c.scrollTop = cbot-ch;
56473         }
56474         
56475         if(hscroll !== false){
56476             if(cleft < sleft){
56477                 c.scrollLeft = cleft;
56478             }else if(cright > sright){
56479                 c.scrollLeft = cright-c.clientWidth;
56480             }
56481         }
56482          
56483         return el;
56484     },
56485
56486     updateColumns : function(){
56487         this.grid.stopEditing();
56488         var cm = this.grid.colModel, colIds = this.getColumnIds();
56489         //var totalWidth = cm.getTotalWidth();
56490         var pos = 0;
56491         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56492             //if(cm.isHidden(i)) continue;
56493             var w = cm.getColumnWidth(i);
56494             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56495             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56496         }
56497         this.updateSplitters();
56498     },
56499
56500     generateRules : function(cm){
56501         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56502         Roo.util.CSS.removeStyleSheet(rulesId);
56503         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56504             var cid = cm.getColumnId(i);
56505             var align = '';
56506             if(cm.config[i].align){
56507                 align = 'text-align:'+cm.config[i].align+';';
56508             }
56509             var hidden = '';
56510             if(cm.isHidden(i)){
56511                 hidden = 'display:none;';
56512             }
56513             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56514             ruleBuf.push(
56515                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56516                     this.hdSelector, cid, " {\n", align, width, "}\n",
56517                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56518                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56519         }
56520         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56521     },
56522
56523     updateSplitters : function(){
56524         var cm = this.cm, s = this.getSplitters();
56525         if(s){ // splitters not created yet
56526             var pos = 0, locked = true;
56527             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56528                 if(cm.isHidden(i)) {
56529                     continue;
56530                 }
56531                 var w = cm.getColumnWidth(i); // make sure it's a number
56532                 if(!cm.isLocked(i) && locked){
56533                     pos = 0;
56534                     locked = false;
56535                 }
56536                 pos += w;
56537                 s[i].style.left = (pos-this.splitOffset) + "px";
56538             }
56539         }
56540     },
56541
56542     handleHiddenChange : function(colModel, colIndex, hidden){
56543         if(hidden){
56544             this.hideColumn(colIndex);
56545         }else{
56546             this.unhideColumn(colIndex);
56547         }
56548     },
56549
56550     hideColumn : function(colIndex){
56551         var cid = this.getColumnId(colIndex);
56552         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56553         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56554         if(Roo.isSafari){
56555             this.updateHeaders();
56556         }
56557         this.updateSplitters();
56558         this.layout();
56559     },
56560
56561     unhideColumn : function(colIndex){
56562         var cid = this.getColumnId(colIndex);
56563         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56564         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56565
56566         if(Roo.isSafari){
56567             this.updateHeaders();
56568         }
56569         this.updateSplitters();
56570         this.layout();
56571     },
56572
56573     insertRows : function(dm, firstRow, lastRow, isUpdate){
56574         if(firstRow == 0 && lastRow == dm.getCount()-1){
56575             this.refresh();
56576         }else{
56577             if(!isUpdate){
56578                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56579             }
56580             var s = this.getScrollState();
56581             var markup = this.renderRows(firstRow, lastRow);
56582             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56583             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56584             this.restoreScroll(s);
56585             if(!isUpdate){
56586                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56587                 this.syncRowHeights(firstRow, lastRow);
56588                 this.stripeRows(firstRow);
56589                 this.layout();
56590             }
56591         }
56592     },
56593
56594     bufferRows : function(markup, target, index){
56595         var before = null, trows = target.rows, tbody = target.tBodies[0];
56596         if(index < trows.length){
56597             before = trows[index];
56598         }
56599         var b = document.createElement("div");
56600         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
56601         var rows = b.firstChild.rows;
56602         for(var i = 0, len = rows.length; i < len; i++){
56603             if(before){
56604                 tbody.insertBefore(rows[0], before);
56605             }else{
56606                 tbody.appendChild(rows[0]);
56607             }
56608         }
56609         b.innerHTML = "";
56610         b = null;
56611     },
56612
56613     deleteRows : function(dm, firstRow, lastRow){
56614         if(dm.getRowCount()<1){
56615             this.fireEvent("beforerefresh", this);
56616             this.mainBody.update("");
56617             this.lockedBody.update("");
56618             this.fireEvent("refresh", this);
56619         }else{
56620             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
56621             var bt = this.getBodyTable();
56622             var tbody = bt.firstChild;
56623             var rows = bt.rows;
56624             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
56625                 tbody.removeChild(rows[firstRow]);
56626             }
56627             this.stripeRows(firstRow);
56628             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
56629         }
56630     },
56631
56632     updateRows : function(dataSource, firstRow, lastRow){
56633         var s = this.getScrollState();
56634         this.refresh();
56635         this.restoreScroll(s);
56636     },
56637
56638     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
56639         if(!noRefresh){
56640            this.refresh();
56641         }
56642         this.updateHeaderSortState();
56643     },
56644
56645     getScrollState : function(){
56646         
56647         var sb = this.scroller.dom;
56648         return {left: sb.scrollLeft, top: sb.scrollTop};
56649     },
56650
56651     stripeRows : function(startRow){
56652         if(!this.grid.stripeRows || this.ds.getCount() < 1){
56653             return;
56654         }
56655         startRow = startRow || 0;
56656         var rows = this.getBodyTable().rows;
56657         var lrows = this.getLockedTable().rows;
56658         var cls = ' x-grid-row-alt ';
56659         for(var i = startRow, len = rows.length; i < len; i++){
56660             var row = rows[i], lrow = lrows[i];
56661             var isAlt = ((i+1) % 2 == 0);
56662             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
56663             if(isAlt == hasAlt){
56664                 continue;
56665             }
56666             if(isAlt){
56667                 row.className += " x-grid-row-alt";
56668             }else{
56669                 row.className = row.className.replace("x-grid-row-alt", "");
56670             }
56671             if(lrow){
56672                 lrow.className = row.className;
56673             }
56674         }
56675     },
56676
56677     restoreScroll : function(state){
56678         //Roo.log('GridView.restoreScroll');
56679         var sb = this.scroller.dom;
56680         sb.scrollLeft = state.left;
56681         sb.scrollTop = state.top;
56682         this.syncScroll();
56683     },
56684
56685     syncScroll : function(){
56686         //Roo.log('GridView.syncScroll');
56687         var sb = this.scroller.dom;
56688         var sh = this.mainHd.dom;
56689         var bs = this.mainBody.dom;
56690         var lv = this.lockedBody.dom;
56691         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
56692         lv.scrollTop = bs.scrollTop = sb.scrollTop;
56693     },
56694
56695     handleScroll : function(e){
56696         this.syncScroll();
56697         var sb = this.scroller.dom;
56698         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
56699         e.stopEvent();
56700     },
56701
56702     handleWheel : function(e){
56703         var d = e.getWheelDelta();
56704         this.scroller.dom.scrollTop -= d*22;
56705         // set this here to prevent jumpy scrolling on large tables
56706         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
56707         e.stopEvent();
56708     },
56709
56710     renderRows : function(startRow, endRow){
56711         // pull in all the crap needed to render rows
56712         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
56713         var colCount = cm.getColumnCount();
56714
56715         if(ds.getCount() < 1){
56716             return ["", ""];
56717         }
56718
56719         // build a map for all the columns
56720         var cs = [];
56721         for(var i = 0; i < colCount; i++){
56722             var name = cm.getDataIndex(i);
56723             cs[i] = {
56724                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
56725                 renderer : cm.getRenderer(i),
56726                 id : cm.getColumnId(i),
56727                 locked : cm.isLocked(i),
56728                 has_editor : cm.isCellEditable(i)
56729             };
56730         }
56731
56732         startRow = startRow || 0;
56733         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
56734
56735         // records to render
56736         var rs = ds.getRange(startRow, endRow);
56737
56738         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
56739     },
56740
56741     // As much as I hate to duplicate code, this was branched because FireFox really hates
56742     // [].join("") on strings. The performance difference was substantial enough to
56743     // branch this function
56744     doRender : Roo.isGecko ?
56745             function(cs, rs, ds, startRow, colCount, stripe){
56746                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56747                 // buffers
56748                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56749                 
56750                 var hasListener = this.grid.hasListener('rowclass');
56751                 var rowcfg = {};
56752                 for(var j = 0, len = rs.length; j < len; j++){
56753                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
56754                     for(var i = 0; i < colCount; i++){
56755                         c = cs[i];
56756                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56757                         p.id = c.id;
56758                         p.css = p.attr = "";
56759                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56760                         if(p.value == undefined || p.value === "") {
56761                             p.value = "&#160;";
56762                         }
56763                         if(c.has_editor){
56764                             p.css += ' x-grid-editable-cell';
56765                         }
56766                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
56767                             p.css +=  ' x-grid-dirty-cell';
56768                         }
56769                         var markup = ct.apply(p);
56770                         if(!c.locked){
56771                             cb+= markup;
56772                         }else{
56773                             lcb+= markup;
56774                         }
56775                     }
56776                     var alt = [];
56777                     if(stripe && ((rowIndex+1) % 2 == 0)){
56778                         alt.push("x-grid-row-alt")
56779                     }
56780                     if(r.dirty){
56781                         alt.push(  " x-grid-dirty-row");
56782                     }
56783                     rp.cells = lcb;
56784                     if(this.getRowClass){
56785                         alt.push(this.getRowClass(r, rowIndex));
56786                     }
56787                     if (hasListener) {
56788                         rowcfg = {
56789                              
56790                             record: r,
56791                             rowIndex : rowIndex,
56792                             rowClass : ''
56793                         };
56794                         this.grid.fireEvent('rowclass', this, rowcfg);
56795                         alt.push(rowcfg.rowClass);
56796                     }
56797                     rp.alt = alt.join(" ");
56798                     lbuf+= rt.apply(rp);
56799                     rp.cells = cb;
56800                     buf+=  rt.apply(rp);
56801                 }
56802                 return [lbuf, buf];
56803             } :
56804             function(cs, rs, ds, startRow, colCount, stripe){
56805                 var ts = this.templates, ct = ts.cell, rt = ts.row;
56806                 // buffers
56807                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
56808                 var hasListener = this.grid.hasListener('rowclass');
56809  
56810                 var rowcfg = {};
56811                 for(var j = 0, len = rs.length; j < len; j++){
56812                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
56813                     for(var i = 0; i < colCount; i++){
56814                         c = cs[i];
56815                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
56816                         p.id = c.id;
56817                         p.css = p.attr = "";
56818                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
56819                         if(p.value == undefined || p.value === "") {
56820                             p.value = "&#160;";
56821                         }
56822                         //Roo.log(c);
56823                          if(c.has_editor){
56824                             p.css += ' x-grid-editable-cell';
56825                         }
56826                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
56827                             p.css += ' x-grid-dirty-cell' 
56828                         }
56829                         
56830                         var markup = ct.apply(p);
56831                         if(!c.locked){
56832                             cb[cb.length] = markup;
56833                         }else{
56834                             lcb[lcb.length] = markup;
56835                         }
56836                     }
56837                     var alt = [];
56838                     if(stripe && ((rowIndex+1) % 2 == 0)){
56839                         alt.push( "x-grid-row-alt");
56840                     }
56841                     if(r.dirty){
56842                         alt.push(" x-grid-dirty-row");
56843                     }
56844                     rp.cells = lcb;
56845                     if(this.getRowClass){
56846                         alt.push( this.getRowClass(r, rowIndex));
56847                     }
56848                     if (hasListener) {
56849                         rowcfg = {
56850                              
56851                             record: r,
56852                             rowIndex : rowIndex,
56853                             rowClass : ''
56854                         };
56855                         this.grid.fireEvent('rowclass', this, rowcfg);
56856                         alt.push(rowcfg.rowClass);
56857                     }
56858                     
56859                     rp.alt = alt.join(" ");
56860                     rp.cells = lcb.join("");
56861                     lbuf[lbuf.length] = rt.apply(rp);
56862                     rp.cells = cb.join("");
56863                     buf[buf.length] =  rt.apply(rp);
56864                 }
56865                 return [lbuf.join(""), buf.join("")];
56866             },
56867
56868     renderBody : function(){
56869         var markup = this.renderRows();
56870         var bt = this.templates.body;
56871         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
56872     },
56873
56874     /**
56875      * Refreshes the grid
56876      * @param {Boolean} headersToo
56877      */
56878     refresh : function(headersToo){
56879         this.fireEvent("beforerefresh", this);
56880         this.grid.stopEditing();
56881         var result = this.renderBody();
56882         this.lockedBody.update(result[0]);
56883         this.mainBody.update(result[1]);
56884         if(headersToo === true){
56885             this.updateHeaders();
56886             this.updateColumns();
56887             this.updateSplitters();
56888             this.updateHeaderSortState();
56889         }
56890         this.syncRowHeights();
56891         this.layout();
56892         this.fireEvent("refresh", this);
56893     },
56894
56895     handleColumnMove : function(cm, oldIndex, newIndex){
56896         this.indexMap = null;
56897         var s = this.getScrollState();
56898         this.refresh(true);
56899         this.restoreScroll(s);
56900         this.afterMove(newIndex);
56901     },
56902
56903     afterMove : function(colIndex){
56904         if(this.enableMoveAnim && Roo.enableFx){
56905             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
56906         }
56907         // if multisort - fix sortOrder, and reload..
56908         if (this.grid.dataSource.multiSort) {
56909             // the we can call sort again..
56910             var dm = this.grid.dataSource;
56911             var cm = this.grid.colModel;
56912             var so = [];
56913             for(var i = 0; i < cm.config.length; i++ ) {
56914                 
56915                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
56916                     continue; // dont' bother, it's not in sort list or being set.
56917                 }
56918                 
56919                 so.push(cm.config[i].dataIndex);
56920             };
56921             dm.sortOrder = so;
56922             dm.load(dm.lastOptions);
56923             
56924             
56925         }
56926         
56927     },
56928
56929     updateCell : function(dm, rowIndex, dataIndex){
56930         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
56931         if(typeof colIndex == "undefined"){ // not present in grid
56932             return;
56933         }
56934         var cm = this.grid.colModel;
56935         var cell = this.getCell(rowIndex, colIndex);
56936         var cellText = this.getCellText(rowIndex, colIndex);
56937
56938         var p = {
56939             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
56940             id : cm.getColumnId(colIndex),
56941             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
56942         };
56943         var renderer = cm.getRenderer(colIndex);
56944         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
56945         if(typeof val == "undefined" || val === "") {
56946             val = "&#160;";
56947         }
56948         cellText.innerHTML = val;
56949         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
56950         this.syncRowHeights(rowIndex, rowIndex);
56951     },
56952
56953     calcColumnWidth : function(colIndex, maxRowsToMeasure){
56954         var maxWidth = 0;
56955         if(this.grid.autoSizeHeaders){
56956             var h = this.getHeaderCellMeasure(colIndex);
56957             maxWidth = Math.max(maxWidth, h.scrollWidth);
56958         }
56959         var tb, index;
56960         if(this.cm.isLocked(colIndex)){
56961             tb = this.getLockedTable();
56962             index = colIndex;
56963         }else{
56964             tb = this.getBodyTable();
56965             index = colIndex - this.cm.getLockedCount();
56966         }
56967         if(tb && tb.rows){
56968             var rows = tb.rows;
56969             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
56970             for(var i = 0; i < stopIndex; i++){
56971                 var cell = rows[i].childNodes[index].firstChild;
56972                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
56973             }
56974         }
56975         return maxWidth + /*margin for error in IE*/ 5;
56976     },
56977     /**
56978      * Autofit a column to its content.
56979      * @param {Number} colIndex
56980      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
56981      */
56982      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
56983          if(this.cm.isHidden(colIndex)){
56984              return; // can't calc a hidden column
56985          }
56986         if(forceMinSize){
56987             var cid = this.cm.getColumnId(colIndex);
56988             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
56989            if(this.grid.autoSizeHeaders){
56990                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
56991            }
56992         }
56993         var newWidth = this.calcColumnWidth(colIndex);
56994         this.cm.setColumnWidth(colIndex,
56995             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
56996         if(!suppressEvent){
56997             this.grid.fireEvent("columnresize", colIndex, newWidth);
56998         }
56999     },
57000
57001     /**
57002      * Autofits all columns to their content and then expands to fit any extra space in the grid
57003      */
57004      autoSizeColumns : function(){
57005         var cm = this.grid.colModel;
57006         var colCount = cm.getColumnCount();
57007         for(var i = 0; i < colCount; i++){
57008             this.autoSizeColumn(i, true, true);
57009         }
57010         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57011             this.fitColumns();
57012         }else{
57013             this.updateColumns();
57014             this.layout();
57015         }
57016     },
57017
57018     /**
57019      * Autofits all columns to the grid's width proportionate with their current size
57020      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57021      */
57022     fitColumns : function(reserveScrollSpace){
57023         var cm = this.grid.colModel;
57024         var colCount = cm.getColumnCount();
57025         var cols = [];
57026         var width = 0;
57027         var i, w;
57028         for (i = 0; i < colCount; i++){
57029             if(!cm.isHidden(i) && !cm.isFixed(i)){
57030                 w = cm.getColumnWidth(i);
57031                 cols.push(i);
57032                 cols.push(w);
57033                 width += w;
57034             }
57035         }
57036         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57037         if(reserveScrollSpace){
57038             avail -= 17;
57039         }
57040         var frac = (avail - cm.getTotalWidth())/width;
57041         while (cols.length){
57042             w = cols.pop();
57043             i = cols.pop();
57044             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57045         }
57046         this.updateColumns();
57047         this.layout();
57048     },
57049
57050     onRowSelect : function(rowIndex){
57051         var row = this.getRowComposite(rowIndex);
57052         row.addClass("x-grid-row-selected");
57053     },
57054
57055     onRowDeselect : function(rowIndex){
57056         var row = this.getRowComposite(rowIndex);
57057         row.removeClass("x-grid-row-selected");
57058     },
57059
57060     onCellSelect : function(row, col){
57061         var cell = this.getCell(row, col);
57062         if(cell){
57063             Roo.fly(cell).addClass("x-grid-cell-selected");
57064         }
57065     },
57066
57067     onCellDeselect : function(row, col){
57068         var cell = this.getCell(row, col);
57069         if(cell){
57070             Roo.fly(cell).removeClass("x-grid-cell-selected");
57071         }
57072     },
57073
57074     updateHeaderSortState : function(){
57075         
57076         // sort state can be single { field: xxx, direction : yyy}
57077         // or   { xxx=>ASC , yyy : DESC ..... }
57078         
57079         var mstate = {};
57080         if (!this.ds.multiSort) { 
57081             var state = this.ds.getSortState();
57082             if(!state){
57083                 return;
57084             }
57085             mstate[state.field] = state.direction;
57086             // FIXME... - this is not used here.. but might be elsewhere..
57087             this.sortState = state;
57088             
57089         } else {
57090             mstate = this.ds.sortToggle;
57091         }
57092         //remove existing sort classes..
57093         
57094         var sc = this.sortClasses;
57095         var hds = this.el.select(this.headerSelector).removeClass(sc);
57096         
57097         for(var f in mstate) {
57098         
57099             var sortColumn = this.cm.findColumnIndex(f);
57100             
57101             if(sortColumn != -1){
57102                 var sortDir = mstate[f];        
57103                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57104             }
57105         }
57106         
57107          
57108         
57109     },
57110
57111
57112     handleHeaderClick : function(g, index,e){
57113         
57114         Roo.log("header click");
57115         
57116         if (Roo.isTouch) {
57117             // touch events on header are handled by context
57118             this.handleHdCtx(g,index,e);
57119             return;
57120         }
57121         
57122         
57123         if(this.headersDisabled){
57124             return;
57125         }
57126         var dm = g.dataSource, cm = g.colModel;
57127         if(!cm.isSortable(index)){
57128             return;
57129         }
57130         g.stopEditing();
57131         
57132         if (dm.multiSort) {
57133             // update the sortOrder
57134             var so = [];
57135             for(var i = 0; i < cm.config.length; i++ ) {
57136                 
57137                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57138                     continue; // dont' bother, it's not in sort list or being set.
57139                 }
57140                 
57141                 so.push(cm.config[i].dataIndex);
57142             };
57143             dm.sortOrder = so;
57144         }
57145         
57146         
57147         dm.sort(cm.getDataIndex(index));
57148     },
57149
57150
57151     destroy : function(){
57152         if(this.colMenu){
57153             this.colMenu.removeAll();
57154             Roo.menu.MenuMgr.unregister(this.colMenu);
57155             this.colMenu.getEl().remove();
57156             delete this.colMenu;
57157         }
57158         if(this.hmenu){
57159             this.hmenu.removeAll();
57160             Roo.menu.MenuMgr.unregister(this.hmenu);
57161             this.hmenu.getEl().remove();
57162             delete this.hmenu;
57163         }
57164         if(this.grid.enableColumnMove){
57165             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57166             if(dds){
57167                 for(var dd in dds){
57168                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57169                         var elid = dds[dd].dragElId;
57170                         dds[dd].unreg();
57171                         Roo.get(elid).remove();
57172                     } else if(dds[dd].config.isTarget){
57173                         dds[dd].proxyTop.remove();
57174                         dds[dd].proxyBottom.remove();
57175                         dds[dd].unreg();
57176                     }
57177                     if(Roo.dd.DDM.locationCache[dd]){
57178                         delete Roo.dd.DDM.locationCache[dd];
57179                     }
57180                 }
57181                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57182             }
57183         }
57184         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57185         this.bind(null, null);
57186         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57187     },
57188
57189     handleLockChange : function(){
57190         this.refresh(true);
57191     },
57192
57193     onDenyColumnLock : function(){
57194
57195     },
57196
57197     onDenyColumnHide : function(){
57198
57199     },
57200
57201     handleHdMenuClick : function(item){
57202         var index = this.hdCtxIndex;
57203         var cm = this.cm, ds = this.ds;
57204         switch(item.id){
57205             case "asc":
57206                 ds.sort(cm.getDataIndex(index), "ASC");
57207                 break;
57208             case "desc":
57209                 ds.sort(cm.getDataIndex(index), "DESC");
57210                 break;
57211             case "lock":
57212                 var lc = cm.getLockedCount();
57213                 if(cm.getColumnCount(true) <= lc+1){
57214                     this.onDenyColumnLock();
57215                     return;
57216                 }
57217                 if(lc != index){
57218                     cm.setLocked(index, true, true);
57219                     cm.moveColumn(index, lc);
57220                     this.grid.fireEvent("columnmove", index, lc);
57221                 }else{
57222                     cm.setLocked(index, true);
57223                 }
57224             break;
57225             case "unlock":
57226                 var lc = cm.getLockedCount();
57227                 if((lc-1) != index){
57228                     cm.setLocked(index, false, true);
57229                     cm.moveColumn(index, lc-1);
57230                     this.grid.fireEvent("columnmove", index, lc-1);
57231                 }else{
57232                     cm.setLocked(index, false);
57233                 }
57234             break;
57235             case 'wider': // used to expand cols on touch..
57236             case 'narrow':
57237                 var cw = cm.getColumnWidth(index);
57238                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57239                 cw = Math.max(0, cw);
57240                 cw = Math.min(cw,4000);
57241                 cm.setColumnWidth(index, cw);
57242                 break;
57243                 
57244             default:
57245                 index = cm.getIndexById(item.id.substr(4));
57246                 if(index != -1){
57247                     if(item.checked && cm.getColumnCount(true) <= 1){
57248                         this.onDenyColumnHide();
57249                         return false;
57250                     }
57251                     cm.setHidden(index, item.checked);
57252                 }
57253         }
57254         return true;
57255     },
57256
57257     beforeColMenuShow : function(){
57258         var cm = this.cm,  colCount = cm.getColumnCount();
57259         this.colMenu.removeAll();
57260         for(var i = 0; i < colCount; i++){
57261             this.colMenu.add(new Roo.menu.CheckItem({
57262                 id: "col-"+cm.getColumnId(i),
57263                 text: cm.getColumnHeader(i),
57264                 checked: !cm.isHidden(i),
57265                 hideOnClick:false
57266             }));
57267         }
57268     },
57269
57270     handleHdCtx : function(g, index, e){
57271         e.stopEvent();
57272         var hd = this.getHeaderCell(index);
57273         this.hdCtxIndex = index;
57274         var ms = this.hmenu.items, cm = this.cm;
57275         ms.get("asc").setDisabled(!cm.isSortable(index));
57276         ms.get("desc").setDisabled(!cm.isSortable(index));
57277         if(this.grid.enableColLock !== false){
57278             ms.get("lock").setDisabled(cm.isLocked(index));
57279             ms.get("unlock").setDisabled(!cm.isLocked(index));
57280         }
57281         this.hmenu.show(hd, "tl-bl");
57282     },
57283
57284     handleHdOver : function(e){
57285         var hd = this.findHeaderCell(e.getTarget());
57286         if(hd && !this.headersDisabled){
57287             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57288                this.fly(hd).addClass("x-grid-hd-over");
57289             }
57290         }
57291     },
57292
57293     handleHdOut : function(e){
57294         var hd = this.findHeaderCell(e.getTarget());
57295         if(hd){
57296             this.fly(hd).removeClass("x-grid-hd-over");
57297         }
57298     },
57299
57300     handleSplitDblClick : function(e, t){
57301         var i = this.getCellIndex(t);
57302         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57303             this.autoSizeColumn(i, true);
57304             this.layout();
57305         }
57306     },
57307
57308     render : function(){
57309
57310         var cm = this.cm;
57311         var colCount = cm.getColumnCount();
57312
57313         if(this.grid.monitorWindowResize === true){
57314             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57315         }
57316         var header = this.renderHeaders();
57317         var body = this.templates.body.apply({rows:""});
57318         var html = this.templates.master.apply({
57319             lockedBody: body,
57320             body: body,
57321             lockedHeader: header[0],
57322             header: header[1]
57323         });
57324
57325         //this.updateColumns();
57326
57327         this.grid.getGridEl().dom.innerHTML = html;
57328
57329         this.initElements();
57330         
57331         // a kludge to fix the random scolling effect in webkit
57332         this.el.on("scroll", function() {
57333             this.el.dom.scrollTop=0; // hopefully not recursive..
57334         },this);
57335
57336         this.scroller.on("scroll", this.handleScroll, this);
57337         this.lockedBody.on("mousewheel", this.handleWheel, this);
57338         this.mainBody.on("mousewheel", this.handleWheel, this);
57339
57340         this.mainHd.on("mouseover", this.handleHdOver, this);
57341         this.mainHd.on("mouseout", this.handleHdOut, this);
57342         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57343                 {delegate: "."+this.splitClass});
57344
57345         this.lockedHd.on("mouseover", this.handleHdOver, this);
57346         this.lockedHd.on("mouseout", this.handleHdOut, this);
57347         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57348                 {delegate: "."+this.splitClass});
57349
57350         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57351             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57352         }
57353
57354         this.updateSplitters();
57355
57356         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57357             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57358             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57359         }
57360
57361         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57362             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57363             this.hmenu.add(
57364                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57365                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57366             );
57367             if(this.grid.enableColLock !== false){
57368                 this.hmenu.add('-',
57369                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57370                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57371                 );
57372             }
57373             if (Roo.isTouch) {
57374                  this.hmenu.add('-',
57375                     {id:"wider", text: this.columnsWiderText},
57376                     {id:"narrow", text: this.columnsNarrowText }
57377                 );
57378                 
57379                  
57380             }
57381             
57382             if(this.grid.enableColumnHide !== false){
57383
57384                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57385                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57386                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57387
57388                 this.hmenu.add('-',
57389                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57390                 );
57391             }
57392             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57393
57394             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57395         }
57396
57397         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57398             this.dd = new Roo.grid.GridDragZone(this.grid, {
57399                 ddGroup : this.grid.ddGroup || 'GridDD'
57400             });
57401             
57402         }
57403
57404         /*
57405         for(var i = 0; i < colCount; i++){
57406             if(cm.isHidden(i)){
57407                 this.hideColumn(i);
57408             }
57409             if(cm.config[i].align){
57410                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57411                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57412             }
57413         }*/
57414         
57415         this.updateHeaderSortState();
57416
57417         this.beforeInitialResize();
57418         this.layout(true);
57419
57420         // two part rendering gives faster view to the user
57421         this.renderPhase2.defer(1, this);
57422     },
57423
57424     renderPhase2 : function(){
57425         // render the rows now
57426         this.refresh();
57427         if(this.grid.autoSizeColumns){
57428             this.autoSizeColumns();
57429         }
57430     },
57431
57432     beforeInitialResize : function(){
57433
57434     },
57435
57436     onColumnSplitterMoved : function(i, w){
57437         this.userResized = true;
57438         var cm = this.grid.colModel;
57439         cm.setColumnWidth(i, w, true);
57440         var cid = cm.getColumnId(i);
57441         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57442         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57443         this.updateSplitters();
57444         this.layout();
57445         this.grid.fireEvent("columnresize", i, w);
57446     },
57447
57448     syncRowHeights : function(startIndex, endIndex){
57449         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57450             startIndex = startIndex || 0;
57451             var mrows = this.getBodyTable().rows;
57452             var lrows = this.getLockedTable().rows;
57453             var len = mrows.length-1;
57454             endIndex = Math.min(endIndex || len, len);
57455             for(var i = startIndex; i <= endIndex; i++){
57456                 var m = mrows[i], l = lrows[i];
57457                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57458                 m.style.height = l.style.height = h + "px";
57459             }
57460         }
57461     },
57462
57463     layout : function(initialRender, is2ndPass){
57464         var g = this.grid;
57465         var auto = g.autoHeight;
57466         var scrollOffset = 16;
57467         var c = g.getGridEl(), cm = this.cm,
57468                 expandCol = g.autoExpandColumn,
57469                 gv = this;
57470         //c.beginMeasure();
57471
57472         if(!c.dom.offsetWidth){ // display:none?
57473             if(initialRender){
57474                 this.lockedWrap.show();
57475                 this.mainWrap.show();
57476             }
57477             return;
57478         }
57479
57480         var hasLock = this.cm.isLocked(0);
57481
57482         var tbh = this.headerPanel.getHeight();
57483         var bbh = this.footerPanel.getHeight();
57484
57485         if(auto){
57486             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57487             var newHeight = ch + c.getBorderWidth("tb");
57488             if(g.maxHeight){
57489                 newHeight = Math.min(g.maxHeight, newHeight);
57490             }
57491             c.setHeight(newHeight);
57492         }
57493
57494         if(g.autoWidth){
57495             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57496         }
57497
57498         var s = this.scroller;
57499
57500         var csize = c.getSize(true);
57501
57502         this.el.setSize(csize.width, csize.height);
57503
57504         this.headerPanel.setWidth(csize.width);
57505         this.footerPanel.setWidth(csize.width);
57506
57507         var hdHeight = this.mainHd.getHeight();
57508         var vw = csize.width;
57509         var vh = csize.height - (tbh + bbh);
57510
57511         s.setSize(vw, vh);
57512
57513         var bt = this.getBodyTable();
57514         
57515         if(cm.getLockedCount() == cm.config.length){
57516             bt = this.getLockedTable();
57517         }
57518         
57519         var ltWidth = hasLock ?
57520                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57521
57522         var scrollHeight = bt.offsetHeight;
57523         var scrollWidth = ltWidth + bt.offsetWidth;
57524         var vscroll = false, hscroll = false;
57525
57526         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57527
57528         var lw = this.lockedWrap, mw = this.mainWrap;
57529         var lb = this.lockedBody, mb = this.mainBody;
57530
57531         setTimeout(function(){
57532             var t = s.dom.offsetTop;
57533             var w = s.dom.clientWidth,
57534                 h = s.dom.clientHeight;
57535
57536             lw.setTop(t);
57537             lw.setSize(ltWidth, h);
57538
57539             mw.setLeftTop(ltWidth, t);
57540             mw.setSize(w-ltWidth, h);
57541
57542             lb.setHeight(h-hdHeight);
57543             mb.setHeight(h-hdHeight);
57544
57545             if(is2ndPass !== true && !gv.userResized && expandCol){
57546                 // high speed resize without full column calculation
57547                 
57548                 var ci = cm.getIndexById(expandCol);
57549                 if (ci < 0) {
57550                     ci = cm.findColumnIndex(expandCol);
57551                 }
57552                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57553                 var expandId = cm.getColumnId(ci);
57554                 var  tw = cm.getTotalWidth(false);
57555                 var currentWidth = cm.getColumnWidth(ci);
57556                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57557                 if(currentWidth != cw){
57558                     cm.setColumnWidth(ci, cw, true);
57559                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57560                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57561                     gv.updateSplitters();
57562                     gv.layout(false, true);
57563                 }
57564             }
57565
57566             if(initialRender){
57567                 lw.show();
57568                 mw.show();
57569             }
57570             //c.endMeasure();
57571         }, 10);
57572     },
57573
57574     onWindowResize : function(){
57575         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57576             return;
57577         }
57578         this.layout();
57579     },
57580
57581     appendFooter : function(parentEl){
57582         return null;
57583     },
57584
57585     sortAscText : "Sort Ascending",
57586     sortDescText : "Sort Descending",
57587     lockText : "Lock Column",
57588     unlockText : "Unlock Column",
57589     columnsText : "Columns",
57590  
57591     columnsWiderText : "Wider",
57592     columnsNarrowText : "Thinner"
57593 });
57594
57595
57596 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
57597     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
57598     this.proxy.el.addClass('x-grid3-col-dd');
57599 };
57600
57601 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
57602     handleMouseDown : function(e){
57603
57604     },
57605
57606     callHandleMouseDown : function(e){
57607         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
57608     }
57609 });
57610 /*
57611  * Based on:
57612  * Ext JS Library 1.1.1
57613  * Copyright(c) 2006-2007, Ext JS, LLC.
57614  *
57615  * Originally Released Under LGPL - original licence link has changed is not relivant.
57616  *
57617  * Fork - LGPL
57618  * <script type="text/javascript">
57619  */
57620  
57621 // private
57622 // This is a support class used internally by the Grid components
57623 Roo.grid.SplitDragZone = function(grid, hd, hd2){
57624     this.grid = grid;
57625     this.view = grid.getView();
57626     this.proxy = this.view.resizeProxy;
57627     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
57628         "gridSplitters" + this.grid.getGridEl().id, {
57629         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
57630     });
57631     this.setHandleElId(Roo.id(hd));
57632     this.setOuterHandleElId(Roo.id(hd2));
57633     this.scroll = false;
57634 };
57635 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
57636     fly: Roo.Element.fly,
57637
57638     b4StartDrag : function(x, y){
57639         this.view.headersDisabled = true;
57640         this.proxy.setHeight(this.view.mainWrap.getHeight());
57641         var w = this.cm.getColumnWidth(this.cellIndex);
57642         var minw = Math.max(w-this.grid.minColumnWidth, 0);
57643         this.resetConstraints();
57644         this.setXConstraint(minw, 1000);
57645         this.setYConstraint(0, 0);
57646         this.minX = x - minw;
57647         this.maxX = x + 1000;
57648         this.startPos = x;
57649         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
57650     },
57651
57652
57653     handleMouseDown : function(e){
57654         ev = Roo.EventObject.setEvent(e);
57655         var t = this.fly(ev.getTarget());
57656         if(t.hasClass("x-grid-split")){
57657             this.cellIndex = this.view.getCellIndex(t.dom);
57658             this.split = t.dom;
57659             this.cm = this.grid.colModel;
57660             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
57661                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
57662             }
57663         }
57664     },
57665
57666     endDrag : function(e){
57667         this.view.headersDisabled = false;
57668         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
57669         var diff = endX - this.startPos;
57670         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
57671     },
57672
57673     autoOffset : function(){
57674         this.setDelta(0,0);
57675     }
57676 });/*
57677  * Based on:
57678  * Ext JS Library 1.1.1
57679  * Copyright(c) 2006-2007, Ext JS, LLC.
57680  *
57681  * Originally Released Under LGPL - original licence link has changed is not relivant.
57682  *
57683  * Fork - LGPL
57684  * <script type="text/javascript">
57685  */
57686  
57687 // private
57688 // This is a support class used internally by the Grid components
57689 Roo.grid.GridDragZone = function(grid, config){
57690     this.view = grid.getView();
57691     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
57692     if(this.view.lockedBody){
57693         this.setHandleElId(Roo.id(this.view.mainBody.dom));
57694         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
57695     }
57696     this.scroll = false;
57697     this.grid = grid;
57698     this.ddel = document.createElement('div');
57699     this.ddel.className = 'x-grid-dd-wrap';
57700 };
57701
57702 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
57703     ddGroup : "GridDD",
57704
57705     getDragData : function(e){
57706         var t = Roo.lib.Event.getTarget(e);
57707         var rowIndex = this.view.findRowIndex(t);
57708         var sm = this.grid.selModel;
57709             
57710         //Roo.log(rowIndex);
57711         
57712         if (sm.getSelectedCell) {
57713             // cell selection..
57714             if (!sm.getSelectedCell()) {
57715                 return false;
57716             }
57717             if (rowIndex != sm.getSelectedCell()[0]) {
57718                 return false;
57719             }
57720         
57721         }
57722         
57723         if(rowIndex !== false){
57724             
57725             // if editorgrid.. 
57726             
57727             
57728             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
57729                
57730             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
57731               //  
57732             //}
57733             if (e.hasModifier()){
57734                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
57735             }
57736             
57737             Roo.log("getDragData");
57738             
57739             return {
57740                 grid: this.grid,
57741                 ddel: this.ddel,
57742                 rowIndex: rowIndex,
57743                 selections:sm.getSelections ? sm.getSelections() : (
57744                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
57745                 )
57746             };
57747         }
57748         return false;
57749     },
57750
57751     onInitDrag : function(e){
57752         var data = this.dragData;
57753         this.ddel.innerHTML = this.grid.getDragDropText();
57754         this.proxy.update(this.ddel);
57755         // fire start drag?
57756     },
57757
57758     afterRepair : function(){
57759         this.dragging = false;
57760     },
57761
57762     getRepairXY : function(e, data){
57763         return false;
57764     },
57765
57766     onEndDrag : function(data, e){
57767         // fire end drag?
57768     },
57769
57770     onValidDrop : function(dd, e, id){
57771         // fire drag drop?
57772         this.hideProxy();
57773     },
57774
57775     beforeInvalidDrop : function(e, id){
57776
57777     }
57778 });/*
57779  * Based on:
57780  * Ext JS Library 1.1.1
57781  * Copyright(c) 2006-2007, Ext JS, LLC.
57782  *
57783  * Originally Released Under LGPL - original licence link has changed is not relivant.
57784  *
57785  * Fork - LGPL
57786  * <script type="text/javascript">
57787  */
57788  
57789
57790 /**
57791  * @class Roo.grid.ColumnModel
57792  * @extends Roo.util.Observable
57793  * This is the default implementation of a ColumnModel used by the Grid. It defines
57794  * the columns in the grid.
57795  * <br>Usage:<br>
57796  <pre><code>
57797  var colModel = new Roo.grid.ColumnModel([
57798         {header: "Ticker", width: 60, sortable: true, locked: true},
57799         {header: "Company Name", width: 150, sortable: true},
57800         {header: "Market Cap.", width: 100, sortable: true},
57801         {header: "$ Sales", width: 100, sortable: true, renderer: money},
57802         {header: "Employees", width: 100, sortable: true, resizable: false}
57803  ]);
57804  </code></pre>
57805  * <p>
57806  
57807  * The config options listed for this class are options which may appear in each
57808  * individual column definition.
57809  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
57810  * @constructor
57811  * @param {Object} config An Array of column config objects. See this class's
57812  * config objects for details.
57813 */
57814 Roo.grid.ColumnModel = function(config){
57815         /**
57816      * The config passed into the constructor
57817      */
57818     this.config = config;
57819     this.lookup = {};
57820
57821     // if no id, create one
57822     // if the column does not have a dataIndex mapping,
57823     // map it to the order it is in the config
57824     for(var i = 0, len = config.length; i < len; i++){
57825         var c = config[i];
57826         if(typeof c.dataIndex == "undefined"){
57827             c.dataIndex = i;
57828         }
57829         if(typeof c.renderer == "string"){
57830             c.renderer = Roo.util.Format[c.renderer];
57831         }
57832         if(typeof c.id == "undefined"){
57833             c.id = Roo.id();
57834         }
57835         if(c.editor && c.editor.xtype){
57836             c.editor  = Roo.factory(c.editor, Roo.grid);
57837         }
57838         if(c.editor && c.editor.isFormField){
57839             c.editor = new Roo.grid.GridEditor(c.editor);
57840         }
57841         this.lookup[c.id] = c;
57842     }
57843
57844     /**
57845      * The width of columns which have no width specified (defaults to 100)
57846      * @type Number
57847      */
57848     this.defaultWidth = 100;
57849
57850     /**
57851      * Default sortable of columns which have no sortable specified (defaults to false)
57852      * @type Boolean
57853      */
57854     this.defaultSortable = false;
57855
57856     this.addEvents({
57857         /**
57858              * @event widthchange
57859              * Fires when the width of a column changes.
57860              * @param {ColumnModel} this
57861              * @param {Number} columnIndex The column index
57862              * @param {Number} newWidth The new width
57863              */
57864             "widthchange": true,
57865         /**
57866              * @event headerchange
57867              * Fires when the text of a header changes.
57868              * @param {ColumnModel} this
57869              * @param {Number} columnIndex The column index
57870              * @param {Number} newText The new header text
57871              */
57872             "headerchange": true,
57873         /**
57874              * @event hiddenchange
57875              * Fires when a column is hidden or "unhidden".
57876              * @param {ColumnModel} this
57877              * @param {Number} columnIndex The column index
57878              * @param {Boolean} hidden true if hidden, false otherwise
57879              */
57880             "hiddenchange": true,
57881             /**
57882          * @event columnmoved
57883          * Fires when a column is moved.
57884          * @param {ColumnModel} this
57885          * @param {Number} oldIndex
57886          * @param {Number} newIndex
57887          */
57888         "columnmoved" : true,
57889         /**
57890          * @event columlockchange
57891          * Fires when a column's locked state is changed
57892          * @param {ColumnModel} this
57893          * @param {Number} colIndex
57894          * @param {Boolean} locked true if locked
57895          */
57896         "columnlockchange" : true
57897     });
57898     Roo.grid.ColumnModel.superclass.constructor.call(this);
57899 };
57900 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
57901     /**
57902      * @cfg {String} header The header text to display in the Grid view.
57903      */
57904     /**
57905      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
57906      * {@link Roo.data.Record} definition from which to draw the column's value. If not
57907      * specified, the column's index is used as an index into the Record's data Array.
57908      */
57909     /**
57910      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
57911      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
57912      */
57913     /**
57914      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
57915      * Defaults to the value of the {@link #defaultSortable} property.
57916      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
57917      */
57918     /**
57919      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
57920      */
57921     /**
57922      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
57923      */
57924     /**
57925      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
57926      */
57927     /**
57928      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
57929      */
57930     /**
57931      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
57932      * given the cell's data value. See {@link #setRenderer}. If not specified, the
57933      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
57934      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
57935      */
57936        /**
57937      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
57938      */
57939     /**
57940      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
57941      */
57942     /**
57943      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
57944      */
57945     /**
57946      * @cfg {String} cursor (Optional)
57947      */
57948     /**
57949      * @cfg {String} tooltip (Optional)
57950      */
57951     /**
57952      * @cfg {Number} xs (Optional)
57953      */
57954     /**
57955      * @cfg {Number} sm (Optional)
57956      */
57957     /**
57958      * @cfg {Number} md (Optional)
57959      */
57960     /**
57961      * @cfg {Number} lg (Optional)
57962      */
57963     /**
57964      * Returns the id of the column at the specified index.
57965      * @param {Number} index The column index
57966      * @return {String} the id
57967      */
57968     getColumnId : function(index){
57969         return this.config[index].id;
57970     },
57971
57972     /**
57973      * Returns the column for a specified id.
57974      * @param {String} id The column id
57975      * @return {Object} the column
57976      */
57977     getColumnById : function(id){
57978         return this.lookup[id];
57979     },
57980
57981     
57982     /**
57983      * Returns the column for a specified dataIndex.
57984      * @param {String} dataIndex The column dataIndex
57985      * @return {Object|Boolean} the column or false if not found
57986      */
57987     getColumnByDataIndex: function(dataIndex){
57988         var index = this.findColumnIndex(dataIndex);
57989         return index > -1 ? this.config[index] : false;
57990     },
57991     
57992     /**
57993      * Returns the index for a specified column id.
57994      * @param {String} id The column id
57995      * @return {Number} the index, or -1 if not found
57996      */
57997     getIndexById : function(id){
57998         for(var i = 0, len = this.config.length; i < len; i++){
57999             if(this.config[i].id == id){
58000                 return i;
58001             }
58002         }
58003         return -1;
58004     },
58005     
58006     /**
58007      * Returns the index for a specified column dataIndex.
58008      * @param {String} dataIndex The column dataIndex
58009      * @return {Number} the index, or -1 if not found
58010      */
58011     
58012     findColumnIndex : function(dataIndex){
58013         for(var i = 0, len = this.config.length; i < len; i++){
58014             if(this.config[i].dataIndex == dataIndex){
58015                 return i;
58016             }
58017         }
58018         return -1;
58019     },
58020     
58021     
58022     moveColumn : function(oldIndex, newIndex){
58023         var c = this.config[oldIndex];
58024         this.config.splice(oldIndex, 1);
58025         this.config.splice(newIndex, 0, c);
58026         this.dataMap = null;
58027         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58028     },
58029
58030     isLocked : function(colIndex){
58031         return this.config[colIndex].locked === true;
58032     },
58033
58034     setLocked : function(colIndex, value, suppressEvent){
58035         if(this.isLocked(colIndex) == value){
58036             return;
58037         }
58038         this.config[colIndex].locked = value;
58039         if(!suppressEvent){
58040             this.fireEvent("columnlockchange", this, colIndex, value);
58041         }
58042     },
58043
58044     getTotalLockedWidth : function(){
58045         var totalWidth = 0;
58046         for(var i = 0; i < this.config.length; i++){
58047             if(this.isLocked(i) && !this.isHidden(i)){
58048                 this.totalWidth += this.getColumnWidth(i);
58049             }
58050         }
58051         return totalWidth;
58052     },
58053
58054     getLockedCount : function(){
58055         for(var i = 0, len = this.config.length; i < len; i++){
58056             if(!this.isLocked(i)){
58057                 return i;
58058             }
58059         }
58060         
58061         return this.config.length;
58062     },
58063
58064     /**
58065      * Returns the number of columns.
58066      * @return {Number}
58067      */
58068     getColumnCount : function(visibleOnly){
58069         if(visibleOnly === true){
58070             var c = 0;
58071             for(var i = 0, len = this.config.length; i < len; i++){
58072                 if(!this.isHidden(i)){
58073                     c++;
58074                 }
58075             }
58076             return c;
58077         }
58078         return this.config.length;
58079     },
58080
58081     /**
58082      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58083      * @param {Function} fn
58084      * @param {Object} scope (optional)
58085      * @return {Array} result
58086      */
58087     getColumnsBy : function(fn, scope){
58088         var r = [];
58089         for(var i = 0, len = this.config.length; i < len; i++){
58090             var c = this.config[i];
58091             if(fn.call(scope||this, c, i) === true){
58092                 r[r.length] = c;
58093             }
58094         }
58095         return r;
58096     },
58097
58098     /**
58099      * Returns true if the specified column is sortable.
58100      * @param {Number} col The column index
58101      * @return {Boolean}
58102      */
58103     isSortable : function(col){
58104         if(typeof this.config[col].sortable == "undefined"){
58105             return this.defaultSortable;
58106         }
58107         return this.config[col].sortable;
58108     },
58109
58110     /**
58111      * Returns the rendering (formatting) function defined for the column.
58112      * @param {Number} col The column index.
58113      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58114      */
58115     getRenderer : function(col){
58116         if(!this.config[col].renderer){
58117             return Roo.grid.ColumnModel.defaultRenderer;
58118         }
58119         return this.config[col].renderer;
58120     },
58121
58122     /**
58123      * Sets the rendering (formatting) function for a column.
58124      * @param {Number} col The column index
58125      * @param {Function} fn The function to use to process the cell's raw data
58126      * to return HTML markup for the grid view. The render function is called with
58127      * the following parameters:<ul>
58128      * <li>Data value.</li>
58129      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58130      * <li>css A CSS style string to apply to the table cell.</li>
58131      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58132      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58133      * <li>Row index</li>
58134      * <li>Column index</li>
58135      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58136      */
58137     setRenderer : function(col, fn){
58138         this.config[col].renderer = fn;
58139     },
58140
58141     /**
58142      * Returns the width for the specified column.
58143      * @param {Number} col The column index
58144      * @return {Number}
58145      */
58146     getColumnWidth : function(col){
58147         return this.config[col].width * 1 || this.defaultWidth;
58148     },
58149
58150     /**
58151      * Sets the width for a column.
58152      * @param {Number} col The column index
58153      * @param {Number} width The new width
58154      */
58155     setColumnWidth : function(col, width, suppressEvent){
58156         this.config[col].width = width;
58157         this.totalWidth = null;
58158         if(!suppressEvent){
58159              this.fireEvent("widthchange", this, col, width);
58160         }
58161     },
58162
58163     /**
58164      * Returns the total width of all columns.
58165      * @param {Boolean} includeHidden True to include hidden column widths
58166      * @return {Number}
58167      */
58168     getTotalWidth : function(includeHidden){
58169         if(!this.totalWidth){
58170             this.totalWidth = 0;
58171             for(var i = 0, len = this.config.length; i < len; i++){
58172                 if(includeHidden || !this.isHidden(i)){
58173                     this.totalWidth += this.getColumnWidth(i);
58174                 }
58175             }
58176         }
58177         return this.totalWidth;
58178     },
58179
58180     /**
58181      * Returns the header for the specified column.
58182      * @param {Number} col The column index
58183      * @return {String}
58184      */
58185     getColumnHeader : function(col){
58186         return this.config[col].header;
58187     },
58188
58189     /**
58190      * Sets the header for a column.
58191      * @param {Number} col The column index
58192      * @param {String} header The new header
58193      */
58194     setColumnHeader : function(col, header){
58195         this.config[col].header = header;
58196         this.fireEvent("headerchange", this, col, header);
58197     },
58198
58199     /**
58200      * Returns the tooltip for the specified column.
58201      * @param {Number} col The column index
58202      * @return {String}
58203      */
58204     getColumnTooltip : function(col){
58205             return this.config[col].tooltip;
58206     },
58207     /**
58208      * Sets the tooltip for a column.
58209      * @param {Number} col The column index
58210      * @param {String} tooltip The new tooltip
58211      */
58212     setColumnTooltip : function(col, tooltip){
58213             this.config[col].tooltip = tooltip;
58214     },
58215
58216     /**
58217      * Returns the dataIndex for the specified column.
58218      * @param {Number} col The column index
58219      * @return {Number}
58220      */
58221     getDataIndex : function(col){
58222         return this.config[col].dataIndex;
58223     },
58224
58225     /**
58226      * Sets the dataIndex for a column.
58227      * @param {Number} col The column index
58228      * @param {Number} dataIndex The new dataIndex
58229      */
58230     setDataIndex : function(col, dataIndex){
58231         this.config[col].dataIndex = dataIndex;
58232     },
58233
58234     
58235     
58236     /**
58237      * Returns true if the cell is editable.
58238      * @param {Number} colIndex The column index
58239      * @param {Number} rowIndex The row index - this is nto actually used..?
58240      * @return {Boolean}
58241      */
58242     isCellEditable : function(colIndex, rowIndex){
58243         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58244     },
58245
58246     /**
58247      * Returns the editor defined for the cell/column.
58248      * return false or null to disable editing.
58249      * @param {Number} colIndex The column index
58250      * @param {Number} rowIndex The row index
58251      * @return {Object}
58252      */
58253     getCellEditor : function(colIndex, rowIndex){
58254         return this.config[colIndex].editor;
58255     },
58256
58257     /**
58258      * Sets if a column is editable.
58259      * @param {Number} col The column index
58260      * @param {Boolean} editable True if the column is editable
58261      */
58262     setEditable : function(col, editable){
58263         this.config[col].editable = editable;
58264     },
58265
58266
58267     /**
58268      * Returns true if the column is hidden.
58269      * @param {Number} colIndex The column index
58270      * @return {Boolean}
58271      */
58272     isHidden : function(colIndex){
58273         return this.config[colIndex].hidden;
58274     },
58275
58276
58277     /**
58278      * Returns true if the column width cannot be changed
58279      */
58280     isFixed : function(colIndex){
58281         return this.config[colIndex].fixed;
58282     },
58283
58284     /**
58285      * Returns true if the column can be resized
58286      * @return {Boolean}
58287      */
58288     isResizable : function(colIndex){
58289         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58290     },
58291     /**
58292      * Sets if a column is hidden.
58293      * @param {Number} colIndex The column index
58294      * @param {Boolean} hidden True if the column is hidden
58295      */
58296     setHidden : function(colIndex, hidden){
58297         this.config[colIndex].hidden = hidden;
58298         this.totalWidth = null;
58299         this.fireEvent("hiddenchange", this, colIndex, hidden);
58300     },
58301
58302     /**
58303      * Sets the editor for a column.
58304      * @param {Number} col The column index
58305      * @param {Object} editor The editor object
58306      */
58307     setEditor : function(col, editor){
58308         this.config[col].editor = editor;
58309     }
58310 });
58311
58312 Roo.grid.ColumnModel.defaultRenderer = function(value)
58313 {
58314     if(typeof value == "object") {
58315         return value;
58316     }
58317         if(typeof value == "string" && value.length < 1){
58318             return "&#160;";
58319         }
58320     
58321         return String.format("{0}", value);
58322 };
58323
58324 // Alias for backwards compatibility
58325 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58326 /*
58327  * Based on:
58328  * Ext JS Library 1.1.1
58329  * Copyright(c) 2006-2007, Ext JS, LLC.
58330  *
58331  * Originally Released Under LGPL - original licence link has changed is not relivant.
58332  *
58333  * Fork - LGPL
58334  * <script type="text/javascript">
58335  */
58336
58337 /**
58338  * @class Roo.grid.AbstractSelectionModel
58339  * @extends Roo.util.Observable
58340  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58341  * implemented by descendant classes.  This class should not be directly instantiated.
58342  * @constructor
58343  */
58344 Roo.grid.AbstractSelectionModel = function(){
58345     this.locked = false;
58346     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58347 };
58348
58349 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58350     /** @ignore Called by the grid automatically. Do not call directly. */
58351     init : function(grid){
58352         this.grid = grid;
58353         this.initEvents();
58354     },
58355
58356     /**
58357      * Locks the selections.
58358      */
58359     lock : function(){
58360         this.locked = true;
58361     },
58362
58363     /**
58364      * Unlocks the selections.
58365      */
58366     unlock : function(){
58367         this.locked = false;
58368     },
58369
58370     /**
58371      * Returns true if the selections are locked.
58372      * @return {Boolean}
58373      */
58374     isLocked : function(){
58375         return this.locked;
58376     }
58377 });/*
58378  * Based on:
58379  * Ext JS Library 1.1.1
58380  * Copyright(c) 2006-2007, Ext JS, LLC.
58381  *
58382  * Originally Released Under LGPL - original licence link has changed is not relivant.
58383  *
58384  * Fork - LGPL
58385  * <script type="text/javascript">
58386  */
58387 /**
58388  * @extends Roo.grid.AbstractSelectionModel
58389  * @class Roo.grid.RowSelectionModel
58390  * The default SelectionModel used by {@link Roo.grid.Grid}.
58391  * It supports multiple selections and keyboard selection/navigation. 
58392  * @constructor
58393  * @param {Object} config
58394  */
58395 Roo.grid.RowSelectionModel = function(config){
58396     Roo.apply(this, config);
58397     this.selections = new Roo.util.MixedCollection(false, function(o){
58398         return o.id;
58399     });
58400
58401     this.last = false;
58402     this.lastActive = false;
58403
58404     this.addEvents({
58405         /**
58406              * @event selectionchange
58407              * Fires when the selection changes
58408              * @param {SelectionModel} this
58409              */
58410             "selectionchange" : true,
58411         /**
58412              * @event afterselectionchange
58413              * Fires after the selection changes (eg. by key press or clicking)
58414              * @param {SelectionModel} this
58415              */
58416             "afterselectionchange" : true,
58417         /**
58418              * @event beforerowselect
58419              * Fires when a row is selected being selected, return false to cancel.
58420              * @param {SelectionModel} this
58421              * @param {Number} rowIndex The selected index
58422              * @param {Boolean} keepExisting False if other selections will be cleared
58423              */
58424             "beforerowselect" : true,
58425         /**
58426              * @event rowselect
58427              * Fires when a row is selected.
58428              * @param {SelectionModel} this
58429              * @param {Number} rowIndex The selected index
58430              * @param {Roo.data.Record} r The record
58431              */
58432             "rowselect" : true,
58433         /**
58434              * @event rowdeselect
58435              * Fires when a row is deselected.
58436              * @param {SelectionModel} this
58437              * @param {Number} rowIndex The selected index
58438              */
58439         "rowdeselect" : true
58440     });
58441     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58442     this.locked = false;
58443 };
58444
58445 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58446     /**
58447      * @cfg {Boolean} singleSelect
58448      * True to allow selection of only one row at a time (defaults to false)
58449      */
58450     singleSelect : false,
58451
58452     // private
58453     initEvents : function(){
58454
58455         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58456             this.grid.on("mousedown", this.handleMouseDown, this);
58457         }else{ // allow click to work like normal
58458             this.grid.on("rowclick", this.handleDragableRowClick, this);
58459         }
58460
58461         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58462             "up" : function(e){
58463                 if(!e.shiftKey){
58464                     this.selectPrevious(e.shiftKey);
58465                 }else if(this.last !== false && this.lastActive !== false){
58466                     var last = this.last;
58467                     this.selectRange(this.last,  this.lastActive-1);
58468                     this.grid.getView().focusRow(this.lastActive);
58469                     if(last !== false){
58470                         this.last = last;
58471                     }
58472                 }else{
58473                     this.selectFirstRow();
58474                 }
58475                 this.fireEvent("afterselectionchange", this);
58476             },
58477             "down" : function(e){
58478                 if(!e.shiftKey){
58479                     this.selectNext(e.shiftKey);
58480                 }else if(this.last !== false && this.lastActive !== false){
58481                     var last = this.last;
58482                     this.selectRange(this.last,  this.lastActive+1);
58483                     this.grid.getView().focusRow(this.lastActive);
58484                     if(last !== false){
58485                         this.last = last;
58486                     }
58487                 }else{
58488                     this.selectFirstRow();
58489                 }
58490                 this.fireEvent("afterselectionchange", this);
58491             },
58492             scope: this
58493         });
58494
58495         var view = this.grid.view;
58496         view.on("refresh", this.onRefresh, this);
58497         view.on("rowupdated", this.onRowUpdated, this);
58498         view.on("rowremoved", this.onRemove, this);
58499     },
58500
58501     // private
58502     onRefresh : function(){
58503         var ds = this.grid.dataSource, i, v = this.grid.view;
58504         var s = this.selections;
58505         s.each(function(r){
58506             if((i = ds.indexOfId(r.id)) != -1){
58507                 v.onRowSelect(i);
58508                 s.add(ds.getAt(i)); // updating the selection relate data
58509             }else{
58510                 s.remove(r);
58511             }
58512         });
58513     },
58514
58515     // private
58516     onRemove : function(v, index, r){
58517         this.selections.remove(r);
58518     },
58519
58520     // private
58521     onRowUpdated : function(v, index, r){
58522         if(this.isSelected(r)){
58523             v.onRowSelect(index);
58524         }
58525     },
58526
58527     /**
58528      * Select records.
58529      * @param {Array} records The records to select
58530      * @param {Boolean} keepExisting (optional) True to keep existing selections
58531      */
58532     selectRecords : function(records, keepExisting){
58533         if(!keepExisting){
58534             this.clearSelections();
58535         }
58536         var ds = this.grid.dataSource;
58537         for(var i = 0, len = records.length; i < len; i++){
58538             this.selectRow(ds.indexOf(records[i]), true);
58539         }
58540     },
58541
58542     /**
58543      * Gets the number of selected rows.
58544      * @return {Number}
58545      */
58546     getCount : function(){
58547         return this.selections.length;
58548     },
58549
58550     /**
58551      * Selects the first row in the grid.
58552      */
58553     selectFirstRow : function(){
58554         this.selectRow(0);
58555     },
58556
58557     /**
58558      * Select the last row.
58559      * @param {Boolean} keepExisting (optional) True to keep existing selections
58560      */
58561     selectLastRow : function(keepExisting){
58562         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58563     },
58564
58565     /**
58566      * Selects the row immediately following the last selected row.
58567      * @param {Boolean} keepExisting (optional) True to keep existing selections
58568      */
58569     selectNext : function(keepExisting){
58570         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58571             this.selectRow(this.last+1, keepExisting);
58572             this.grid.getView().focusRow(this.last);
58573         }
58574     },
58575
58576     /**
58577      * Selects the row that precedes the last selected row.
58578      * @param {Boolean} keepExisting (optional) True to keep existing selections
58579      */
58580     selectPrevious : function(keepExisting){
58581         if(this.last){
58582             this.selectRow(this.last-1, keepExisting);
58583             this.grid.getView().focusRow(this.last);
58584         }
58585     },
58586
58587     /**
58588      * Returns the selected records
58589      * @return {Array} Array of selected records
58590      */
58591     getSelections : function(){
58592         return [].concat(this.selections.items);
58593     },
58594
58595     /**
58596      * Returns the first selected record.
58597      * @return {Record}
58598      */
58599     getSelected : function(){
58600         return this.selections.itemAt(0);
58601     },
58602
58603
58604     /**
58605      * Clears all selections.
58606      */
58607     clearSelections : function(fast){
58608         if(this.locked) {
58609             return;
58610         }
58611         if(fast !== true){
58612             var ds = this.grid.dataSource;
58613             var s = this.selections;
58614             s.each(function(r){
58615                 this.deselectRow(ds.indexOfId(r.id));
58616             }, this);
58617             s.clear();
58618         }else{
58619             this.selections.clear();
58620         }
58621         this.last = false;
58622     },
58623
58624
58625     /**
58626      * Selects all rows.
58627      */
58628     selectAll : function(){
58629         if(this.locked) {
58630             return;
58631         }
58632         this.selections.clear();
58633         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
58634             this.selectRow(i, true);
58635         }
58636     },
58637
58638     /**
58639      * Returns True if there is a selection.
58640      * @return {Boolean}
58641      */
58642     hasSelection : function(){
58643         return this.selections.length > 0;
58644     },
58645
58646     /**
58647      * Returns True if the specified row is selected.
58648      * @param {Number/Record} record The record or index of the record to check
58649      * @return {Boolean}
58650      */
58651     isSelected : function(index){
58652         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
58653         return (r && this.selections.key(r.id) ? true : false);
58654     },
58655
58656     /**
58657      * Returns True if the specified record id is selected.
58658      * @param {String} id The id of record to check
58659      * @return {Boolean}
58660      */
58661     isIdSelected : function(id){
58662         return (this.selections.key(id) ? true : false);
58663     },
58664
58665     // private
58666     handleMouseDown : function(e, t){
58667         var view = this.grid.getView(), rowIndex;
58668         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
58669             return;
58670         };
58671         if(e.shiftKey && this.last !== false){
58672             var last = this.last;
58673             this.selectRange(last, rowIndex, e.ctrlKey);
58674             this.last = last; // reset the last
58675             view.focusRow(rowIndex);
58676         }else{
58677             var isSelected = this.isSelected(rowIndex);
58678             if(e.button !== 0 && isSelected){
58679                 view.focusRow(rowIndex);
58680             }else if(e.ctrlKey && isSelected){
58681                 this.deselectRow(rowIndex);
58682             }else if(!isSelected){
58683                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
58684                 view.focusRow(rowIndex);
58685             }
58686         }
58687         this.fireEvent("afterselectionchange", this);
58688     },
58689     // private
58690     handleDragableRowClick :  function(grid, rowIndex, e) 
58691     {
58692         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
58693             this.selectRow(rowIndex, false);
58694             grid.view.focusRow(rowIndex);
58695              this.fireEvent("afterselectionchange", this);
58696         }
58697     },
58698     
58699     /**
58700      * Selects multiple rows.
58701      * @param {Array} rows Array of the indexes of the row to select
58702      * @param {Boolean} keepExisting (optional) True to keep existing selections
58703      */
58704     selectRows : function(rows, keepExisting){
58705         if(!keepExisting){
58706             this.clearSelections();
58707         }
58708         for(var i = 0, len = rows.length; i < len; i++){
58709             this.selectRow(rows[i], true);
58710         }
58711     },
58712
58713     /**
58714      * Selects a range of rows. All rows in between startRow and endRow are also selected.
58715      * @param {Number} startRow The index of the first row in the range
58716      * @param {Number} endRow The index of the last row in the range
58717      * @param {Boolean} keepExisting (optional) True to retain existing selections
58718      */
58719     selectRange : function(startRow, endRow, keepExisting){
58720         if(this.locked) {
58721             return;
58722         }
58723         if(!keepExisting){
58724             this.clearSelections();
58725         }
58726         if(startRow <= endRow){
58727             for(var i = startRow; i <= endRow; i++){
58728                 this.selectRow(i, true);
58729             }
58730         }else{
58731             for(var i = startRow; i >= endRow; i--){
58732                 this.selectRow(i, true);
58733             }
58734         }
58735     },
58736
58737     /**
58738      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
58739      * @param {Number} startRow The index of the first row in the range
58740      * @param {Number} endRow The index of the last row in the range
58741      */
58742     deselectRange : function(startRow, endRow, preventViewNotify){
58743         if(this.locked) {
58744             return;
58745         }
58746         for(var i = startRow; i <= endRow; i++){
58747             this.deselectRow(i, preventViewNotify);
58748         }
58749     },
58750
58751     /**
58752      * Selects a row.
58753      * @param {Number} row The index of the row to select
58754      * @param {Boolean} keepExisting (optional) True to keep existing selections
58755      */
58756     selectRow : function(index, keepExisting, preventViewNotify){
58757         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
58758             return;
58759         }
58760         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
58761             if(!keepExisting || this.singleSelect){
58762                 this.clearSelections();
58763             }
58764             var r = this.grid.dataSource.getAt(index);
58765             this.selections.add(r);
58766             this.last = this.lastActive = index;
58767             if(!preventViewNotify){
58768                 this.grid.getView().onRowSelect(index);
58769             }
58770             this.fireEvent("rowselect", this, index, r);
58771             this.fireEvent("selectionchange", this);
58772         }
58773     },
58774
58775     /**
58776      * Deselects a row.
58777      * @param {Number} row The index of the row to deselect
58778      */
58779     deselectRow : function(index, preventViewNotify){
58780         if(this.locked) {
58781             return;
58782         }
58783         if(this.last == index){
58784             this.last = false;
58785         }
58786         if(this.lastActive == index){
58787             this.lastActive = false;
58788         }
58789         var r = this.grid.dataSource.getAt(index);
58790         this.selections.remove(r);
58791         if(!preventViewNotify){
58792             this.grid.getView().onRowDeselect(index);
58793         }
58794         this.fireEvent("rowdeselect", this, index);
58795         this.fireEvent("selectionchange", this);
58796     },
58797
58798     // private
58799     restoreLast : function(){
58800         if(this._last){
58801             this.last = this._last;
58802         }
58803     },
58804
58805     // private
58806     acceptsNav : function(row, col, cm){
58807         return !cm.isHidden(col) && cm.isCellEditable(col, row);
58808     },
58809
58810     // private
58811     onEditorKey : function(field, e){
58812         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
58813         if(k == e.TAB){
58814             e.stopEvent();
58815             ed.completeEdit();
58816             if(e.shiftKey){
58817                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
58818             }else{
58819                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
58820             }
58821         }else if(k == e.ENTER && !e.ctrlKey){
58822             e.stopEvent();
58823             ed.completeEdit();
58824             if(e.shiftKey){
58825                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
58826             }else{
58827                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
58828             }
58829         }else if(k == e.ESC){
58830             ed.cancelEdit();
58831         }
58832         if(newCell){
58833             g.startEditing(newCell[0], newCell[1]);
58834         }
58835     }
58836 });/*
58837  * Based on:
58838  * Ext JS Library 1.1.1
58839  * Copyright(c) 2006-2007, Ext JS, LLC.
58840  *
58841  * Originally Released Under LGPL - original licence link has changed is not relivant.
58842  *
58843  * Fork - LGPL
58844  * <script type="text/javascript">
58845  */
58846 /**
58847  * @class Roo.grid.CellSelectionModel
58848  * @extends Roo.grid.AbstractSelectionModel
58849  * This class provides the basic implementation for cell selection in a grid.
58850  * @constructor
58851  * @param {Object} config The object containing the configuration of this model.
58852  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
58853  */
58854 Roo.grid.CellSelectionModel = function(config){
58855     Roo.apply(this, config);
58856
58857     this.selection = null;
58858
58859     this.addEvents({
58860         /**
58861              * @event beforerowselect
58862              * Fires before a cell is selected.
58863              * @param {SelectionModel} this
58864              * @param {Number} rowIndex The selected row index
58865              * @param {Number} colIndex The selected cell index
58866              */
58867             "beforecellselect" : true,
58868         /**
58869              * @event cellselect
58870              * Fires when a cell is selected.
58871              * @param {SelectionModel} this
58872              * @param {Number} rowIndex The selected row index
58873              * @param {Number} colIndex The selected cell index
58874              */
58875             "cellselect" : true,
58876         /**
58877              * @event selectionchange
58878              * Fires when the active selection changes.
58879              * @param {SelectionModel} this
58880              * @param {Object} selection null for no selection or an object (o) with two properties
58881                 <ul>
58882                 <li>o.record: the record object for the row the selection is in</li>
58883                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
58884                 </ul>
58885              */
58886             "selectionchange" : true,
58887         /**
58888              * @event tabend
58889              * Fires when the tab (or enter) was pressed on the last editable cell
58890              * You can use this to trigger add new row.
58891              * @param {SelectionModel} this
58892              */
58893             "tabend" : true,
58894          /**
58895              * @event beforeeditnext
58896              * Fires before the next editable sell is made active
58897              * You can use this to skip to another cell or fire the tabend
58898              *    if you set cell to false
58899              * @param {Object} eventdata object : { cell : [ row, col ] } 
58900              */
58901             "beforeeditnext" : true
58902     });
58903     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
58904 };
58905
58906 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
58907     
58908     enter_is_tab: false,
58909
58910     /** @ignore */
58911     initEvents : function(){
58912         this.grid.on("mousedown", this.handleMouseDown, this);
58913         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
58914         var view = this.grid.view;
58915         view.on("refresh", this.onViewChange, this);
58916         view.on("rowupdated", this.onRowUpdated, this);
58917         view.on("beforerowremoved", this.clearSelections, this);
58918         view.on("beforerowsinserted", this.clearSelections, this);
58919         if(this.grid.isEditor){
58920             this.grid.on("beforeedit", this.beforeEdit,  this);
58921         }
58922     },
58923
58924         //private
58925     beforeEdit : function(e){
58926         this.select(e.row, e.column, false, true, e.record);
58927     },
58928
58929         //private
58930     onRowUpdated : function(v, index, r){
58931         if(this.selection && this.selection.record == r){
58932             v.onCellSelect(index, this.selection.cell[1]);
58933         }
58934     },
58935
58936         //private
58937     onViewChange : function(){
58938         this.clearSelections(true);
58939     },
58940
58941         /**
58942          * Returns the currently selected cell,.
58943          * @return {Array} The selected cell (row, column) or null if none selected.
58944          */
58945     getSelectedCell : function(){
58946         return this.selection ? this.selection.cell : null;
58947     },
58948
58949     /**
58950      * Clears all selections.
58951      * @param {Boolean} true to prevent the gridview from being notified about the change.
58952      */
58953     clearSelections : function(preventNotify){
58954         var s = this.selection;
58955         if(s){
58956             if(preventNotify !== true){
58957                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
58958             }
58959             this.selection = null;
58960             this.fireEvent("selectionchange", this, null);
58961         }
58962     },
58963
58964     /**
58965      * Returns true if there is a selection.
58966      * @return {Boolean}
58967      */
58968     hasSelection : function(){
58969         return this.selection ? true : false;
58970     },
58971
58972     /** @ignore */
58973     handleMouseDown : function(e, t){
58974         var v = this.grid.getView();
58975         if(this.isLocked()){
58976             return;
58977         };
58978         var row = v.findRowIndex(t);
58979         var cell = v.findCellIndex(t);
58980         if(row !== false && cell !== false){
58981             this.select(row, cell);
58982         }
58983     },
58984
58985     /**
58986      * Selects a cell.
58987      * @param {Number} rowIndex
58988      * @param {Number} collIndex
58989      */
58990     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
58991         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
58992             this.clearSelections();
58993             r = r || this.grid.dataSource.getAt(rowIndex);
58994             this.selection = {
58995                 record : r,
58996                 cell : [rowIndex, colIndex]
58997             };
58998             if(!preventViewNotify){
58999                 var v = this.grid.getView();
59000                 v.onCellSelect(rowIndex, colIndex);
59001                 if(preventFocus !== true){
59002                     v.focusCell(rowIndex, colIndex);
59003                 }
59004             }
59005             this.fireEvent("cellselect", this, rowIndex, colIndex);
59006             this.fireEvent("selectionchange", this, this.selection);
59007         }
59008     },
59009
59010         //private
59011     isSelectable : function(rowIndex, colIndex, cm){
59012         return !cm.isHidden(colIndex);
59013     },
59014
59015     /** @ignore */
59016     handleKeyDown : function(e){
59017         //Roo.log('Cell Sel Model handleKeyDown');
59018         if(!e.isNavKeyPress()){
59019             return;
59020         }
59021         var g = this.grid, s = this.selection;
59022         if(!s){
59023             e.stopEvent();
59024             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59025             if(cell){
59026                 this.select(cell[0], cell[1]);
59027             }
59028             return;
59029         }
59030         var sm = this;
59031         var walk = function(row, col, step){
59032             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59033         };
59034         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59035         var newCell;
59036
59037       
59038
59039         switch(k){
59040             case e.TAB:
59041                 // handled by onEditorKey
59042                 if (g.isEditor && g.editing) {
59043                     return;
59044                 }
59045                 if(e.shiftKey) {
59046                     newCell = walk(r, c-1, -1);
59047                 } else {
59048                     newCell = walk(r, c+1, 1);
59049                 }
59050                 break;
59051             
59052             case e.DOWN:
59053                newCell = walk(r+1, c, 1);
59054                 break;
59055             
59056             case e.UP:
59057                 newCell = walk(r-1, c, -1);
59058                 break;
59059             
59060             case e.RIGHT:
59061                 newCell = walk(r, c+1, 1);
59062                 break;
59063             
59064             case e.LEFT:
59065                 newCell = walk(r, c-1, -1);
59066                 break;
59067             
59068             case e.ENTER:
59069                 
59070                 if(g.isEditor && !g.editing){
59071                    g.startEditing(r, c);
59072                    e.stopEvent();
59073                    return;
59074                 }
59075                 
59076                 
59077              break;
59078         };
59079         if(newCell){
59080             this.select(newCell[0], newCell[1]);
59081             e.stopEvent();
59082             
59083         }
59084     },
59085
59086     acceptsNav : function(row, col, cm){
59087         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59088     },
59089     /**
59090      * Selects a cell.
59091      * @param {Number} field (not used) - as it's normally used as a listener
59092      * @param {Number} e - event - fake it by using
59093      *
59094      * var e = Roo.EventObjectImpl.prototype;
59095      * e.keyCode = e.TAB
59096      *
59097      * 
59098      */
59099     onEditorKey : function(field, e){
59100         
59101         var k = e.getKey(),
59102             newCell,
59103             g = this.grid,
59104             ed = g.activeEditor,
59105             forward = false;
59106         ///Roo.log('onEditorKey' + k);
59107         
59108         
59109         if (this.enter_is_tab && k == e.ENTER) {
59110             k = e.TAB;
59111         }
59112         
59113         if(k == e.TAB){
59114             if(e.shiftKey){
59115                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59116             }else{
59117                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59118                 forward = true;
59119             }
59120             
59121             e.stopEvent();
59122             
59123         } else if(k == e.ENTER &&  !e.ctrlKey){
59124             ed.completeEdit();
59125             e.stopEvent();
59126             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59127         
59128                 } else if(k == e.ESC){
59129             ed.cancelEdit();
59130         }
59131                 
59132         if (newCell) {
59133             var ecall = { cell : newCell, forward : forward };
59134             this.fireEvent('beforeeditnext', ecall );
59135             newCell = ecall.cell;
59136                         forward = ecall.forward;
59137         }
59138                 
59139         if(newCell){
59140             //Roo.log('next cell after edit');
59141             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59142         } else if (forward) {
59143             // tabbed past last
59144             this.fireEvent.defer(100, this, ['tabend',this]);
59145         }
59146     }
59147 });/*
59148  * Based on:
59149  * Ext JS Library 1.1.1
59150  * Copyright(c) 2006-2007, Ext JS, LLC.
59151  *
59152  * Originally Released Under LGPL - original licence link has changed is not relivant.
59153  *
59154  * Fork - LGPL
59155  * <script type="text/javascript">
59156  */
59157  
59158 /**
59159  * @class Roo.grid.EditorGrid
59160  * @extends Roo.grid.Grid
59161  * Class for creating and editable grid.
59162  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59163  * The container MUST have some type of size defined for the grid to fill. The container will be 
59164  * automatically set to position relative if it isn't already.
59165  * @param {Object} dataSource The data model to bind to
59166  * @param {Object} colModel The column model with info about this grid's columns
59167  */
59168 Roo.grid.EditorGrid = function(container, config){
59169     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59170     this.getGridEl().addClass("xedit-grid");
59171
59172     if(!this.selModel){
59173         this.selModel = new Roo.grid.CellSelectionModel();
59174     }
59175
59176     this.activeEditor = null;
59177
59178         this.addEvents({
59179             /**
59180              * @event beforeedit
59181              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59182              * <ul style="padding:5px;padding-left:16px;">
59183              * <li>grid - This grid</li>
59184              * <li>record - The record being edited</li>
59185              * <li>field - The field name being edited</li>
59186              * <li>value - The value for the field being edited.</li>
59187              * <li>row - The grid row index</li>
59188              * <li>column - The grid column index</li>
59189              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59190              * </ul>
59191              * @param {Object} e An edit event (see above for description)
59192              */
59193             "beforeedit" : true,
59194             /**
59195              * @event afteredit
59196              * Fires after a cell is edited. <br />
59197              * <ul style="padding:5px;padding-left:16px;">
59198              * <li>grid - This grid</li>
59199              * <li>record - The record being edited</li>
59200              * <li>field - The field name being edited</li>
59201              * <li>value - The value being set</li>
59202              * <li>originalValue - The original value for the field, before the edit.</li>
59203              * <li>row - The grid row index</li>
59204              * <li>column - The grid column index</li>
59205              * </ul>
59206              * @param {Object} e An edit event (see above for description)
59207              */
59208             "afteredit" : true,
59209             /**
59210              * @event validateedit
59211              * Fires after a cell is edited, but before the value is set in the record. 
59212          * You can use this to modify the value being set in the field, Return false
59213              * to cancel the change. The edit event object has the following properties <br />
59214              * <ul style="padding:5px;padding-left:16px;">
59215          * <li>editor - This editor</li>
59216              * <li>grid - This grid</li>
59217              * <li>record - The record being edited</li>
59218              * <li>field - The field name being edited</li>
59219              * <li>value - The value being set</li>
59220              * <li>originalValue - The original value for the field, before the edit.</li>
59221              * <li>row - The grid row index</li>
59222              * <li>column - The grid column index</li>
59223              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59224              * </ul>
59225              * @param {Object} e An edit event (see above for description)
59226              */
59227             "validateedit" : true
59228         });
59229     this.on("bodyscroll", this.stopEditing,  this);
59230     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59231 };
59232
59233 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59234     /**
59235      * @cfg {Number} clicksToEdit
59236      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59237      */
59238     clicksToEdit: 2,
59239
59240     // private
59241     isEditor : true,
59242     // private
59243     trackMouseOver: false, // causes very odd FF errors
59244
59245     onCellDblClick : function(g, row, col){
59246         this.startEditing(row, col);
59247     },
59248
59249     onEditComplete : function(ed, value, startValue){
59250         this.editing = false;
59251         this.activeEditor = null;
59252         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59253         var r = ed.record;
59254         var field = this.colModel.getDataIndex(ed.col);
59255         var e = {
59256             grid: this,
59257             record: r,
59258             field: field,
59259             originalValue: startValue,
59260             value: value,
59261             row: ed.row,
59262             column: ed.col,
59263             cancel:false,
59264             editor: ed
59265         };
59266         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59267         cell.show();
59268           
59269         if(String(value) !== String(startValue)){
59270             
59271             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59272                 r.set(field, e.value);
59273                 // if we are dealing with a combo box..
59274                 // then we also set the 'name' colum to be the displayField
59275                 if (ed.field.displayField && ed.field.name) {
59276                     r.set(ed.field.name, ed.field.el.dom.value);
59277                 }
59278                 
59279                 delete e.cancel; //?? why!!!
59280                 this.fireEvent("afteredit", e);
59281             }
59282         } else {
59283             this.fireEvent("afteredit", e); // always fire it!
59284         }
59285         this.view.focusCell(ed.row, ed.col);
59286     },
59287
59288     /**
59289      * Starts editing the specified for the specified row/column
59290      * @param {Number} rowIndex
59291      * @param {Number} colIndex
59292      */
59293     startEditing : function(row, col){
59294         this.stopEditing();
59295         if(this.colModel.isCellEditable(col, row)){
59296             this.view.ensureVisible(row, col, true);
59297           
59298             var r = this.dataSource.getAt(row);
59299             var field = this.colModel.getDataIndex(col);
59300             var cell = Roo.get(this.view.getCell(row,col));
59301             var e = {
59302                 grid: this,
59303                 record: r,
59304                 field: field,
59305                 value: r.data[field],
59306                 row: row,
59307                 column: col,
59308                 cancel:false 
59309             };
59310             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59311                 this.editing = true;
59312                 var ed = this.colModel.getCellEditor(col, row);
59313                 
59314                 if (!ed) {
59315                     return;
59316                 }
59317                 if(!ed.rendered){
59318                     ed.render(ed.parentEl || document.body);
59319                 }
59320                 ed.field.reset();
59321                
59322                 cell.hide();
59323                 
59324                 (function(){ // complex but required for focus issues in safari, ie and opera
59325                     ed.row = row;
59326                     ed.col = col;
59327                     ed.record = r;
59328                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59329                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59330                     this.activeEditor = ed;
59331                     var v = r.data[field];
59332                     ed.startEdit(this.view.getCell(row, col), v);
59333                     // combo's with 'displayField and name set
59334                     if (ed.field.displayField && ed.field.name) {
59335                         ed.field.el.dom.value = r.data[ed.field.name];
59336                     }
59337                     
59338                     
59339                 }).defer(50, this);
59340             }
59341         }
59342     },
59343         
59344     /**
59345      * Stops any active editing
59346      */
59347     stopEditing : function(){
59348         if(this.activeEditor){
59349             this.activeEditor.completeEdit();
59350         }
59351         this.activeEditor = null;
59352     },
59353         
59354          /**
59355      * Called to get grid's drag proxy text, by default returns this.ddText.
59356      * @return {String}
59357      */
59358     getDragDropText : function(){
59359         var count = this.selModel.getSelectedCell() ? 1 : 0;
59360         return String.format(this.ddText, count, count == 1 ? '' : 's');
59361     }
59362         
59363 });/*
59364  * Based on:
59365  * Ext JS Library 1.1.1
59366  * Copyright(c) 2006-2007, Ext JS, LLC.
59367  *
59368  * Originally Released Under LGPL - original licence link has changed is not relivant.
59369  *
59370  * Fork - LGPL
59371  * <script type="text/javascript">
59372  */
59373
59374 // private - not really -- you end up using it !
59375 // This is a support class used internally by the Grid components
59376
59377 /**
59378  * @class Roo.grid.GridEditor
59379  * @extends Roo.Editor
59380  * Class for creating and editable grid elements.
59381  * @param {Object} config any settings (must include field)
59382  */
59383 Roo.grid.GridEditor = function(field, config){
59384     if (!config && field.field) {
59385         config = field;
59386         field = Roo.factory(config.field, Roo.form);
59387     }
59388     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59389     field.monitorTab = false;
59390 };
59391
59392 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59393     
59394     /**
59395      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59396      */
59397     
59398     alignment: "tl-tl",
59399     autoSize: "width",
59400     hideEl : false,
59401     cls: "x-small-editor x-grid-editor",
59402     shim:false,
59403     shadow:"frame"
59404 });/*
59405  * Based on:
59406  * Ext JS Library 1.1.1
59407  * Copyright(c) 2006-2007, Ext JS, LLC.
59408  *
59409  * Originally Released Under LGPL - original licence link has changed is not relivant.
59410  *
59411  * Fork - LGPL
59412  * <script type="text/javascript">
59413  */
59414   
59415
59416   
59417 Roo.grid.PropertyRecord = Roo.data.Record.create([
59418     {name:'name',type:'string'},  'value'
59419 ]);
59420
59421
59422 Roo.grid.PropertyStore = function(grid, source){
59423     this.grid = grid;
59424     this.store = new Roo.data.Store({
59425         recordType : Roo.grid.PropertyRecord
59426     });
59427     this.store.on('update', this.onUpdate,  this);
59428     if(source){
59429         this.setSource(source);
59430     }
59431     Roo.grid.PropertyStore.superclass.constructor.call(this);
59432 };
59433
59434
59435
59436 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59437     setSource : function(o){
59438         this.source = o;
59439         this.store.removeAll();
59440         var data = [];
59441         for(var k in o){
59442             if(this.isEditableValue(o[k])){
59443                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59444             }
59445         }
59446         this.store.loadRecords({records: data}, {}, true);
59447     },
59448
59449     onUpdate : function(ds, record, type){
59450         if(type == Roo.data.Record.EDIT){
59451             var v = record.data['value'];
59452             var oldValue = record.modified['value'];
59453             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59454                 this.source[record.id] = v;
59455                 record.commit();
59456                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59457             }else{
59458                 record.reject();
59459             }
59460         }
59461     },
59462
59463     getProperty : function(row){
59464        return this.store.getAt(row);
59465     },
59466
59467     isEditableValue: function(val){
59468         if(val && val instanceof Date){
59469             return true;
59470         }else if(typeof val == 'object' || typeof val == 'function'){
59471             return false;
59472         }
59473         return true;
59474     },
59475
59476     setValue : function(prop, value){
59477         this.source[prop] = value;
59478         this.store.getById(prop).set('value', value);
59479     },
59480
59481     getSource : function(){
59482         return this.source;
59483     }
59484 });
59485
59486 Roo.grid.PropertyColumnModel = function(grid, store){
59487     this.grid = grid;
59488     var g = Roo.grid;
59489     g.PropertyColumnModel.superclass.constructor.call(this, [
59490         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59491         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59492     ]);
59493     this.store = store;
59494     this.bselect = Roo.DomHelper.append(document.body, {
59495         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59496             {tag: 'option', value: 'true', html: 'true'},
59497             {tag: 'option', value: 'false', html: 'false'}
59498         ]
59499     });
59500     Roo.id(this.bselect);
59501     var f = Roo.form;
59502     this.editors = {
59503         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59504         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59505         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59506         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59507         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59508     };
59509     this.renderCellDelegate = this.renderCell.createDelegate(this);
59510     this.renderPropDelegate = this.renderProp.createDelegate(this);
59511 };
59512
59513 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59514     
59515     
59516     nameText : 'Name',
59517     valueText : 'Value',
59518     
59519     dateFormat : 'm/j/Y',
59520     
59521     
59522     renderDate : function(dateVal){
59523         return dateVal.dateFormat(this.dateFormat);
59524     },
59525
59526     renderBool : function(bVal){
59527         return bVal ? 'true' : 'false';
59528     },
59529
59530     isCellEditable : function(colIndex, rowIndex){
59531         return colIndex == 1;
59532     },
59533
59534     getRenderer : function(col){
59535         return col == 1 ?
59536             this.renderCellDelegate : this.renderPropDelegate;
59537     },
59538
59539     renderProp : function(v){
59540         return this.getPropertyName(v);
59541     },
59542
59543     renderCell : function(val){
59544         var rv = val;
59545         if(val instanceof Date){
59546             rv = this.renderDate(val);
59547         }else if(typeof val == 'boolean'){
59548             rv = this.renderBool(val);
59549         }
59550         return Roo.util.Format.htmlEncode(rv);
59551     },
59552
59553     getPropertyName : function(name){
59554         var pn = this.grid.propertyNames;
59555         return pn && pn[name] ? pn[name] : name;
59556     },
59557
59558     getCellEditor : function(colIndex, rowIndex){
59559         var p = this.store.getProperty(rowIndex);
59560         var n = p.data['name'], val = p.data['value'];
59561         
59562         if(typeof(this.grid.customEditors[n]) == 'string'){
59563             return this.editors[this.grid.customEditors[n]];
59564         }
59565         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59566             return this.grid.customEditors[n];
59567         }
59568         if(val instanceof Date){
59569             return this.editors['date'];
59570         }else if(typeof val == 'number'){
59571             return this.editors['number'];
59572         }else if(typeof val == 'boolean'){
59573             return this.editors['boolean'];
59574         }else{
59575             return this.editors['string'];
59576         }
59577     }
59578 });
59579
59580 /**
59581  * @class Roo.grid.PropertyGrid
59582  * @extends Roo.grid.EditorGrid
59583  * This class represents the  interface of a component based property grid control.
59584  * <br><br>Usage:<pre><code>
59585  var grid = new Roo.grid.PropertyGrid("my-container-id", {
59586       
59587  });
59588  // set any options
59589  grid.render();
59590  * </code></pre>
59591   
59592  * @constructor
59593  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59594  * The container MUST have some type of size defined for the grid to fill. The container will be
59595  * automatically set to position relative if it isn't already.
59596  * @param {Object} config A config object that sets properties on this grid.
59597  */
59598 Roo.grid.PropertyGrid = function(container, config){
59599     config = config || {};
59600     var store = new Roo.grid.PropertyStore(this);
59601     this.store = store;
59602     var cm = new Roo.grid.PropertyColumnModel(this, store);
59603     store.store.sort('name', 'ASC');
59604     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
59605         ds: store.store,
59606         cm: cm,
59607         enableColLock:false,
59608         enableColumnMove:false,
59609         stripeRows:false,
59610         trackMouseOver: false,
59611         clicksToEdit:1
59612     }, config));
59613     this.getGridEl().addClass('x-props-grid');
59614     this.lastEditRow = null;
59615     this.on('columnresize', this.onColumnResize, this);
59616     this.addEvents({
59617          /**
59618              * @event beforepropertychange
59619              * Fires before a property changes (return false to stop?)
59620              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59621              * @param {String} id Record Id
59622              * @param {String} newval New Value
59623          * @param {String} oldval Old Value
59624              */
59625         "beforepropertychange": true,
59626         /**
59627              * @event propertychange
59628              * Fires after a property changes
59629              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
59630              * @param {String} id Record Id
59631              * @param {String} newval New Value
59632          * @param {String} oldval Old Value
59633              */
59634         "propertychange": true
59635     });
59636     this.customEditors = this.customEditors || {};
59637 };
59638 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
59639     
59640      /**
59641      * @cfg {Object} customEditors map of colnames=> custom editors.
59642      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
59643      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
59644      * false disables editing of the field.
59645          */
59646     
59647       /**
59648      * @cfg {Object} propertyNames map of property Names to their displayed value
59649          */
59650     
59651     render : function(){
59652         Roo.grid.PropertyGrid.superclass.render.call(this);
59653         this.autoSize.defer(100, this);
59654     },
59655
59656     autoSize : function(){
59657         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
59658         if(this.view){
59659             this.view.fitColumns();
59660         }
59661     },
59662
59663     onColumnResize : function(){
59664         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
59665         this.autoSize();
59666     },
59667     /**
59668      * Sets the data for the Grid
59669      * accepts a Key => Value object of all the elements avaiable.
59670      * @param {Object} data  to appear in grid.
59671      */
59672     setSource : function(source){
59673         this.store.setSource(source);
59674         //this.autoSize();
59675     },
59676     /**
59677      * Gets all the data from the grid.
59678      * @return {Object} data  data stored in grid
59679      */
59680     getSource : function(){
59681         return this.store.getSource();
59682     }
59683 });/*
59684   
59685  * Licence LGPL
59686  
59687  */
59688  
59689 /**
59690  * @class Roo.grid.Calendar
59691  * @extends Roo.util.Grid
59692  * This class extends the Grid to provide a calendar widget
59693  * <br><br>Usage:<pre><code>
59694  var grid = new Roo.grid.Calendar("my-container-id", {
59695      ds: myDataStore,
59696      cm: myColModel,
59697      selModel: mySelectionModel,
59698      autoSizeColumns: true,
59699      monitorWindowResize: false,
59700      trackMouseOver: true
59701      eventstore : real data store..
59702  });
59703  // set any options
59704  grid.render();
59705   
59706   * @constructor
59707  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
59708  * The container MUST have some type of size defined for the grid to fill. The container will be
59709  * automatically set to position relative if it isn't already.
59710  * @param {Object} config A config object that sets properties on this grid.
59711  */
59712 Roo.grid.Calendar = function(container, config){
59713         // initialize the container
59714         this.container = Roo.get(container);
59715         this.container.update("");
59716         this.container.setStyle("overflow", "hidden");
59717     this.container.addClass('x-grid-container');
59718
59719     this.id = this.container.id;
59720
59721     Roo.apply(this, config);
59722     // check and correct shorthanded configs
59723     
59724     var rows = [];
59725     var d =1;
59726     for (var r = 0;r < 6;r++) {
59727         
59728         rows[r]=[];
59729         for (var c =0;c < 7;c++) {
59730             rows[r][c]= '';
59731         }
59732     }
59733     if (this.eventStore) {
59734         this.eventStore= Roo.factory(this.eventStore, Roo.data);
59735         this.eventStore.on('load',this.onLoad, this);
59736         this.eventStore.on('beforeload',this.clearEvents, this);
59737          
59738     }
59739     
59740     this.dataSource = new Roo.data.Store({
59741             proxy: new Roo.data.MemoryProxy(rows),
59742             reader: new Roo.data.ArrayReader({}, [
59743                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
59744     });
59745
59746     this.dataSource.load();
59747     this.ds = this.dataSource;
59748     this.ds.xmodule = this.xmodule || false;
59749     
59750     
59751     var cellRender = function(v,x,r)
59752     {
59753         return String.format(
59754             '<div class="fc-day  fc-widget-content"><div>' +
59755                 '<div class="fc-event-container"></div>' +
59756                 '<div class="fc-day-number">{0}</div>'+
59757                 
59758                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
59759             '</div></div>', v);
59760     
59761     }
59762     
59763     
59764     this.colModel = new Roo.grid.ColumnModel( [
59765         {
59766             xtype: 'ColumnModel',
59767             xns: Roo.grid,
59768             dataIndex : 'weekday0',
59769             header : 'Sunday',
59770             renderer : cellRender
59771         },
59772         {
59773             xtype: 'ColumnModel',
59774             xns: Roo.grid,
59775             dataIndex : 'weekday1',
59776             header : 'Monday',
59777             renderer : cellRender
59778         },
59779         {
59780             xtype: 'ColumnModel',
59781             xns: Roo.grid,
59782             dataIndex : 'weekday2',
59783             header : 'Tuesday',
59784             renderer : cellRender
59785         },
59786         {
59787             xtype: 'ColumnModel',
59788             xns: Roo.grid,
59789             dataIndex : 'weekday3',
59790             header : 'Wednesday',
59791             renderer : cellRender
59792         },
59793         {
59794             xtype: 'ColumnModel',
59795             xns: Roo.grid,
59796             dataIndex : 'weekday4',
59797             header : 'Thursday',
59798             renderer : cellRender
59799         },
59800         {
59801             xtype: 'ColumnModel',
59802             xns: Roo.grid,
59803             dataIndex : 'weekday5',
59804             header : 'Friday',
59805             renderer : cellRender
59806         },
59807         {
59808             xtype: 'ColumnModel',
59809             xns: Roo.grid,
59810             dataIndex : 'weekday6',
59811             header : 'Saturday',
59812             renderer : cellRender
59813         }
59814     ]);
59815     this.cm = this.colModel;
59816     this.cm.xmodule = this.xmodule || false;
59817  
59818         
59819           
59820     //this.selModel = new Roo.grid.CellSelectionModel();
59821     //this.sm = this.selModel;
59822     //this.selModel.init(this);
59823     
59824     
59825     if(this.width){
59826         this.container.setWidth(this.width);
59827     }
59828
59829     if(this.height){
59830         this.container.setHeight(this.height);
59831     }
59832     /** @private */
59833         this.addEvents({
59834         // raw events
59835         /**
59836          * @event click
59837          * The raw click event for the entire grid.
59838          * @param {Roo.EventObject} e
59839          */
59840         "click" : true,
59841         /**
59842          * @event dblclick
59843          * The raw dblclick event for the entire grid.
59844          * @param {Roo.EventObject} e
59845          */
59846         "dblclick" : true,
59847         /**
59848          * @event contextmenu
59849          * The raw contextmenu event for the entire grid.
59850          * @param {Roo.EventObject} e
59851          */
59852         "contextmenu" : true,
59853         /**
59854          * @event mousedown
59855          * The raw mousedown event for the entire grid.
59856          * @param {Roo.EventObject} e
59857          */
59858         "mousedown" : true,
59859         /**
59860          * @event mouseup
59861          * The raw mouseup event for the entire grid.
59862          * @param {Roo.EventObject} e
59863          */
59864         "mouseup" : true,
59865         /**
59866          * @event mouseover
59867          * The raw mouseover event for the entire grid.
59868          * @param {Roo.EventObject} e
59869          */
59870         "mouseover" : true,
59871         /**
59872          * @event mouseout
59873          * The raw mouseout event for the entire grid.
59874          * @param {Roo.EventObject} e
59875          */
59876         "mouseout" : true,
59877         /**
59878          * @event keypress
59879          * The raw keypress event for the entire grid.
59880          * @param {Roo.EventObject} e
59881          */
59882         "keypress" : true,
59883         /**
59884          * @event keydown
59885          * The raw keydown event for the entire grid.
59886          * @param {Roo.EventObject} e
59887          */
59888         "keydown" : true,
59889
59890         // custom events
59891
59892         /**
59893          * @event cellclick
59894          * Fires when a cell is clicked
59895          * @param {Grid} this
59896          * @param {Number} rowIndex
59897          * @param {Number} columnIndex
59898          * @param {Roo.EventObject} e
59899          */
59900         "cellclick" : true,
59901         /**
59902          * @event celldblclick
59903          * Fires when a cell is double clicked
59904          * @param {Grid} this
59905          * @param {Number} rowIndex
59906          * @param {Number} columnIndex
59907          * @param {Roo.EventObject} e
59908          */
59909         "celldblclick" : true,
59910         /**
59911          * @event rowclick
59912          * Fires when a row is clicked
59913          * @param {Grid} this
59914          * @param {Number} rowIndex
59915          * @param {Roo.EventObject} e
59916          */
59917         "rowclick" : true,
59918         /**
59919          * @event rowdblclick
59920          * Fires when a row is double clicked
59921          * @param {Grid} this
59922          * @param {Number} rowIndex
59923          * @param {Roo.EventObject} e
59924          */
59925         "rowdblclick" : true,
59926         /**
59927          * @event headerclick
59928          * Fires when a header is clicked
59929          * @param {Grid} this
59930          * @param {Number} columnIndex
59931          * @param {Roo.EventObject} e
59932          */
59933         "headerclick" : true,
59934         /**
59935          * @event headerdblclick
59936          * Fires when a header cell is double clicked
59937          * @param {Grid} this
59938          * @param {Number} columnIndex
59939          * @param {Roo.EventObject} e
59940          */
59941         "headerdblclick" : true,
59942         /**
59943          * @event rowcontextmenu
59944          * Fires when a row is right clicked
59945          * @param {Grid} this
59946          * @param {Number} rowIndex
59947          * @param {Roo.EventObject} e
59948          */
59949         "rowcontextmenu" : true,
59950         /**
59951          * @event cellcontextmenu
59952          * Fires when a cell is right clicked
59953          * @param {Grid} this
59954          * @param {Number} rowIndex
59955          * @param {Number} cellIndex
59956          * @param {Roo.EventObject} e
59957          */
59958          "cellcontextmenu" : true,
59959         /**
59960          * @event headercontextmenu
59961          * Fires when a header is right clicked
59962          * @param {Grid} this
59963          * @param {Number} columnIndex
59964          * @param {Roo.EventObject} e
59965          */
59966         "headercontextmenu" : true,
59967         /**
59968          * @event bodyscroll
59969          * Fires when the body element is scrolled
59970          * @param {Number} scrollLeft
59971          * @param {Number} scrollTop
59972          */
59973         "bodyscroll" : true,
59974         /**
59975          * @event columnresize
59976          * Fires when the user resizes a column
59977          * @param {Number} columnIndex
59978          * @param {Number} newSize
59979          */
59980         "columnresize" : true,
59981         /**
59982          * @event columnmove
59983          * Fires when the user moves a column
59984          * @param {Number} oldIndex
59985          * @param {Number} newIndex
59986          */
59987         "columnmove" : true,
59988         /**
59989          * @event startdrag
59990          * Fires when row(s) start being dragged
59991          * @param {Grid} this
59992          * @param {Roo.GridDD} dd The drag drop object
59993          * @param {event} e The raw browser event
59994          */
59995         "startdrag" : true,
59996         /**
59997          * @event enddrag
59998          * Fires when a drag operation is complete
59999          * @param {Grid} this
60000          * @param {Roo.GridDD} dd The drag drop object
60001          * @param {event} e The raw browser event
60002          */
60003         "enddrag" : true,
60004         /**
60005          * @event dragdrop
60006          * Fires when dragged row(s) are dropped on a valid DD target
60007          * @param {Grid} this
60008          * @param {Roo.GridDD} dd The drag drop object
60009          * @param {String} targetId The target drag drop object
60010          * @param {event} e The raw browser event
60011          */
60012         "dragdrop" : true,
60013         /**
60014          * @event dragover
60015          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60016          * @param {Grid} this
60017          * @param {Roo.GridDD} dd The drag drop object
60018          * @param {String} targetId The target drag drop object
60019          * @param {event} e The raw browser event
60020          */
60021         "dragover" : true,
60022         /**
60023          * @event dragenter
60024          *  Fires when the dragged row(s) first cross another DD target while being dragged
60025          * @param {Grid} this
60026          * @param {Roo.GridDD} dd The drag drop object
60027          * @param {String} targetId The target drag drop object
60028          * @param {event} e The raw browser event
60029          */
60030         "dragenter" : true,
60031         /**
60032          * @event dragout
60033          * Fires when the dragged row(s) leave another DD target while being dragged
60034          * @param {Grid} this
60035          * @param {Roo.GridDD} dd The drag drop object
60036          * @param {String} targetId The target drag drop object
60037          * @param {event} e The raw browser event
60038          */
60039         "dragout" : true,
60040         /**
60041          * @event rowclass
60042          * Fires when a row is rendered, so you can change add a style to it.
60043          * @param {GridView} gridview   The grid view
60044          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60045          */
60046         'rowclass' : true,
60047
60048         /**
60049          * @event render
60050          * Fires when the grid is rendered
60051          * @param {Grid} grid
60052          */
60053         'render' : true,
60054             /**
60055              * @event select
60056              * Fires when a date is selected
60057              * @param {DatePicker} this
60058              * @param {Date} date The selected date
60059              */
60060         'select': true,
60061         /**
60062              * @event monthchange
60063              * Fires when the displayed month changes 
60064              * @param {DatePicker} this
60065              * @param {Date} date The selected month
60066              */
60067         'monthchange': true,
60068         /**
60069              * @event evententer
60070              * Fires when mouse over an event
60071              * @param {Calendar} this
60072              * @param {event} Event
60073              */
60074         'evententer': true,
60075         /**
60076              * @event eventleave
60077              * Fires when the mouse leaves an
60078              * @param {Calendar} this
60079              * @param {event}
60080              */
60081         'eventleave': true,
60082         /**
60083              * @event eventclick
60084              * Fires when the mouse click an
60085              * @param {Calendar} this
60086              * @param {event}
60087              */
60088         'eventclick': true,
60089         /**
60090              * @event eventrender
60091              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60092              * @param {Calendar} this
60093              * @param {data} data to be modified
60094              */
60095         'eventrender': true
60096         
60097     });
60098
60099     Roo.grid.Grid.superclass.constructor.call(this);
60100     this.on('render', function() {
60101         this.view.el.addClass('x-grid-cal'); 
60102         
60103         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60104
60105     },this);
60106     
60107     if (!Roo.grid.Calendar.style) {
60108         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60109             
60110             
60111             '.x-grid-cal .x-grid-col' :  {
60112                 height: 'auto !important',
60113                 'vertical-align': 'top'
60114             },
60115             '.x-grid-cal  .fc-event-hori' : {
60116                 height: '14px'
60117             }
60118              
60119             
60120         }, Roo.id());
60121     }
60122
60123     
60124     
60125 };
60126 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60127     /**
60128      * @cfg {Store} eventStore The store that loads events.
60129      */
60130     eventStore : 25,
60131
60132      
60133     activeDate : false,
60134     startDay : 0,
60135     autoWidth : true,
60136     monitorWindowResize : false,
60137
60138     
60139     resizeColumns : function() {
60140         var col = (this.view.el.getWidth() / 7) - 3;
60141         // loop through cols, and setWidth
60142         for(var i =0 ; i < 7 ; i++){
60143             this.cm.setColumnWidth(i, col);
60144         }
60145     },
60146      setDate :function(date) {
60147         
60148         Roo.log('setDate?');
60149         
60150         this.resizeColumns();
60151         var vd = this.activeDate;
60152         this.activeDate = date;
60153 //        if(vd && this.el){
60154 //            var t = date.getTime();
60155 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60156 //                Roo.log('using add remove');
60157 //                
60158 //                this.fireEvent('monthchange', this, date);
60159 //                
60160 //                this.cells.removeClass("fc-state-highlight");
60161 //                this.cells.each(function(c){
60162 //                   if(c.dateValue == t){
60163 //                       c.addClass("fc-state-highlight");
60164 //                       setTimeout(function(){
60165 //                            try{c.dom.firstChild.focus();}catch(e){}
60166 //                       }, 50);
60167 //                       return false;
60168 //                   }
60169 //                   return true;
60170 //                });
60171 //                return;
60172 //            }
60173 //        }
60174         
60175         var days = date.getDaysInMonth();
60176         
60177         var firstOfMonth = date.getFirstDateOfMonth();
60178         var startingPos = firstOfMonth.getDay()-this.startDay;
60179         
60180         if(startingPos < this.startDay){
60181             startingPos += 7;
60182         }
60183         
60184         var pm = date.add(Date.MONTH, -1);
60185         var prevStart = pm.getDaysInMonth()-startingPos;
60186 //        
60187         
60188         
60189         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60190         
60191         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60192         //this.cells.addClassOnOver('fc-state-hover');
60193         
60194         var cells = this.cells.elements;
60195         var textEls = this.textNodes;
60196         
60197         //Roo.each(cells, function(cell){
60198         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60199         //});
60200         
60201         days += startingPos;
60202
60203         // convert everything to numbers so it's fast
60204         var day = 86400000;
60205         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60206         //Roo.log(d);
60207         //Roo.log(pm);
60208         //Roo.log(prevStart);
60209         
60210         var today = new Date().clearTime().getTime();
60211         var sel = date.clearTime().getTime();
60212         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60213         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60214         var ddMatch = this.disabledDatesRE;
60215         var ddText = this.disabledDatesText;
60216         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60217         var ddaysText = this.disabledDaysText;
60218         var format = this.format;
60219         
60220         var setCellClass = function(cal, cell){
60221             
60222             //Roo.log('set Cell Class');
60223             cell.title = "";
60224             var t = d.getTime();
60225             
60226             //Roo.log(d);
60227             
60228             
60229             cell.dateValue = t;
60230             if(t == today){
60231                 cell.className += " fc-today";
60232                 cell.className += " fc-state-highlight";
60233                 cell.title = cal.todayText;
60234             }
60235             if(t == sel){
60236                 // disable highlight in other month..
60237                 cell.className += " fc-state-highlight";
60238                 
60239             }
60240             // disabling
60241             if(t < min) {
60242                 //cell.className = " fc-state-disabled";
60243                 cell.title = cal.minText;
60244                 return;
60245             }
60246             if(t > max) {
60247                 //cell.className = " fc-state-disabled";
60248                 cell.title = cal.maxText;
60249                 return;
60250             }
60251             if(ddays){
60252                 if(ddays.indexOf(d.getDay()) != -1){
60253                     // cell.title = ddaysText;
60254                    // cell.className = " fc-state-disabled";
60255                 }
60256             }
60257             if(ddMatch && format){
60258                 var fvalue = d.dateFormat(format);
60259                 if(ddMatch.test(fvalue)){
60260                     cell.title = ddText.replace("%0", fvalue);
60261                    cell.className = " fc-state-disabled";
60262                 }
60263             }
60264             
60265             if (!cell.initialClassName) {
60266                 cell.initialClassName = cell.dom.className;
60267             }
60268             
60269             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60270         };
60271
60272         var i = 0;
60273         
60274         for(; i < startingPos; i++) {
60275             cells[i].dayName =  (++prevStart);
60276             Roo.log(textEls[i]);
60277             d.setDate(d.getDate()+1);
60278             
60279             //cells[i].className = "fc-past fc-other-month";
60280             setCellClass(this, cells[i]);
60281         }
60282         
60283         var intDay = 0;
60284         
60285         for(; i < days; i++){
60286             intDay = i - startingPos + 1;
60287             cells[i].dayName =  (intDay);
60288             d.setDate(d.getDate()+1);
60289             
60290             cells[i].className = ''; // "x-date-active";
60291             setCellClass(this, cells[i]);
60292         }
60293         var extraDays = 0;
60294         
60295         for(; i < 42; i++) {
60296             //textEls[i].innerHTML = (++extraDays);
60297             
60298             d.setDate(d.getDate()+1);
60299             cells[i].dayName = (++extraDays);
60300             cells[i].className = "fc-future fc-other-month";
60301             setCellClass(this, cells[i]);
60302         }
60303         
60304         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60305         
60306         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60307         
60308         // this will cause all the cells to mis
60309         var rows= [];
60310         var i =0;
60311         for (var r = 0;r < 6;r++) {
60312             for (var c =0;c < 7;c++) {
60313                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60314             }    
60315         }
60316         
60317         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60318         for(i=0;i<cells.length;i++) {
60319             
60320             this.cells.elements[i].dayName = cells[i].dayName ;
60321             this.cells.elements[i].className = cells[i].className;
60322             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60323             this.cells.elements[i].title = cells[i].title ;
60324             this.cells.elements[i].dateValue = cells[i].dateValue ;
60325         }
60326         
60327         
60328         
60329         
60330         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60331         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60332         
60333         ////if(totalRows != 6){
60334             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60335            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60336        // }
60337         
60338         this.fireEvent('monthchange', this, date);
60339         
60340         
60341     },
60342  /**
60343      * Returns the grid's SelectionModel.
60344      * @return {SelectionModel}
60345      */
60346     getSelectionModel : function(){
60347         if(!this.selModel){
60348             this.selModel = new Roo.grid.CellSelectionModel();
60349         }
60350         return this.selModel;
60351     },
60352
60353     load: function() {
60354         this.eventStore.load()
60355         
60356         
60357         
60358     },
60359     
60360     findCell : function(dt) {
60361         dt = dt.clearTime().getTime();
60362         var ret = false;
60363         this.cells.each(function(c){
60364             //Roo.log("check " +c.dateValue + '?=' + dt);
60365             if(c.dateValue == dt){
60366                 ret = c;
60367                 return false;
60368             }
60369             return true;
60370         });
60371         
60372         return ret;
60373     },
60374     
60375     findCells : function(rec) {
60376         var s = rec.data.start_dt.clone().clearTime().getTime();
60377        // Roo.log(s);
60378         var e= rec.data.end_dt.clone().clearTime().getTime();
60379        // Roo.log(e);
60380         var ret = [];
60381         this.cells.each(function(c){
60382              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60383             
60384             if(c.dateValue > e){
60385                 return ;
60386             }
60387             if(c.dateValue < s){
60388                 return ;
60389             }
60390             ret.push(c);
60391         });
60392         
60393         return ret;    
60394     },
60395     
60396     findBestRow: function(cells)
60397     {
60398         var ret = 0;
60399         
60400         for (var i =0 ; i < cells.length;i++) {
60401             ret  = Math.max(cells[i].rows || 0,ret);
60402         }
60403         return ret;
60404         
60405     },
60406     
60407     
60408     addItem : function(rec)
60409     {
60410         // look for vertical location slot in
60411         var cells = this.findCells(rec);
60412         
60413         rec.row = this.findBestRow(cells);
60414         
60415         // work out the location.
60416         
60417         var crow = false;
60418         var rows = [];
60419         for(var i =0; i < cells.length; i++) {
60420             if (!crow) {
60421                 crow = {
60422                     start : cells[i],
60423                     end :  cells[i]
60424                 };
60425                 continue;
60426             }
60427             if (crow.start.getY() == cells[i].getY()) {
60428                 // on same row.
60429                 crow.end = cells[i];
60430                 continue;
60431             }
60432             // different row.
60433             rows.push(crow);
60434             crow = {
60435                 start: cells[i],
60436                 end : cells[i]
60437             };
60438             
60439         }
60440         
60441         rows.push(crow);
60442         rec.els = [];
60443         rec.rows = rows;
60444         rec.cells = cells;
60445         for (var i = 0; i < cells.length;i++) {
60446             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60447             
60448         }
60449         
60450         
60451     },
60452     
60453     clearEvents: function() {
60454         
60455         if (!this.eventStore.getCount()) {
60456             return;
60457         }
60458         // reset number of rows in cells.
60459         Roo.each(this.cells.elements, function(c){
60460             c.rows = 0;
60461         });
60462         
60463         this.eventStore.each(function(e) {
60464             this.clearEvent(e);
60465         },this);
60466         
60467     },
60468     
60469     clearEvent : function(ev)
60470     {
60471         if (ev.els) {
60472             Roo.each(ev.els, function(el) {
60473                 el.un('mouseenter' ,this.onEventEnter, this);
60474                 el.un('mouseleave' ,this.onEventLeave, this);
60475                 el.remove();
60476             },this);
60477             ev.els = [];
60478         }
60479     },
60480     
60481     
60482     renderEvent : function(ev,ctr) {
60483         if (!ctr) {
60484              ctr = this.view.el.select('.fc-event-container',true).first();
60485         }
60486         
60487          
60488         this.clearEvent(ev);
60489             //code
60490        
60491         
60492         
60493         ev.els = [];
60494         var cells = ev.cells;
60495         var rows = ev.rows;
60496         this.fireEvent('eventrender', this, ev);
60497         
60498         for(var i =0; i < rows.length; i++) {
60499             
60500             cls = '';
60501             if (i == 0) {
60502                 cls += ' fc-event-start';
60503             }
60504             if ((i+1) == rows.length) {
60505                 cls += ' fc-event-end';
60506             }
60507             
60508             //Roo.log(ev.data);
60509             // how many rows should it span..
60510             var cg = this.eventTmpl.append(ctr,Roo.apply({
60511                 fccls : cls
60512                 
60513             }, ev.data) , true);
60514             
60515             
60516             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60517             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60518             cg.on('click', this.onEventClick, this, ev);
60519             
60520             ev.els.push(cg);
60521             
60522             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60523             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60524             //Roo.log(cg);
60525              
60526             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60527             cg.setWidth(ebox.right - sbox.x -2);
60528         }
60529     },
60530     
60531     renderEvents: function()
60532     {   
60533         // first make sure there is enough space..
60534         
60535         if (!this.eventTmpl) {
60536             this.eventTmpl = new Roo.Template(
60537                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60538                     '<div class="fc-event-inner">' +
60539                         '<span class="fc-event-time">{time}</span>' +
60540                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60541                     '</div>' +
60542                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60543                 '</div>'
60544             );
60545                 
60546         }
60547                
60548         
60549         
60550         this.cells.each(function(c) {
60551             //Roo.log(c.select('.fc-day-content div',true).first());
60552             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60553         });
60554         
60555         var ctr = this.view.el.select('.fc-event-container',true).first();
60556         
60557         var cls;
60558         this.eventStore.each(function(ev){
60559             
60560             this.renderEvent(ev);
60561              
60562              
60563         }, this);
60564         this.view.layout();
60565         
60566     },
60567     
60568     onEventEnter: function (e, el,event,d) {
60569         this.fireEvent('evententer', this, el, event);
60570     },
60571     
60572     onEventLeave: function (e, el,event,d) {
60573         this.fireEvent('eventleave', this, el, event);
60574     },
60575     
60576     onEventClick: function (e, el,event,d) {
60577         this.fireEvent('eventclick', this, el, event);
60578     },
60579     
60580     onMonthChange: function () {
60581         this.store.load();
60582     },
60583     
60584     onLoad: function () {
60585         
60586         //Roo.log('calendar onload');
60587 //         
60588         if(this.eventStore.getCount() > 0){
60589             
60590            
60591             
60592             this.eventStore.each(function(d){
60593                 
60594                 
60595                 // FIXME..
60596                 var add =   d.data;
60597                 if (typeof(add.end_dt) == 'undefined')  {
60598                     Roo.log("Missing End time in calendar data: ");
60599                     Roo.log(d);
60600                     return;
60601                 }
60602                 if (typeof(add.start_dt) == 'undefined')  {
60603                     Roo.log("Missing Start time in calendar data: ");
60604                     Roo.log(d);
60605                     return;
60606                 }
60607                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
60608                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
60609                 add.id = add.id || d.id;
60610                 add.title = add.title || '??';
60611                 
60612                 this.addItem(d);
60613                 
60614              
60615             },this);
60616         }
60617         
60618         this.renderEvents();
60619     }
60620     
60621
60622 });
60623 /*
60624  grid : {
60625                 xtype: 'Grid',
60626                 xns: Roo.grid,
60627                 listeners : {
60628                     render : function ()
60629                     {
60630                         _this.grid = this;
60631                         
60632                         if (!this.view.el.hasClass('course-timesheet')) {
60633                             this.view.el.addClass('course-timesheet');
60634                         }
60635                         if (this.tsStyle) {
60636                             this.ds.load({});
60637                             return; 
60638                         }
60639                         Roo.log('width');
60640                         Roo.log(_this.grid.view.el.getWidth());
60641                         
60642                         
60643                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
60644                             '.course-timesheet .x-grid-row' : {
60645                                 height: '80px'
60646                             },
60647                             '.x-grid-row td' : {
60648                                 'vertical-align' : 0
60649                             },
60650                             '.course-edit-link' : {
60651                                 'color' : 'blue',
60652                                 'text-overflow' : 'ellipsis',
60653                                 'overflow' : 'hidden',
60654                                 'white-space' : 'nowrap',
60655                                 'cursor' : 'pointer'
60656                             },
60657                             '.sub-link' : {
60658                                 'color' : 'green'
60659                             },
60660                             '.de-act-sup-link' : {
60661                                 'color' : 'purple',
60662                                 'text-decoration' : 'line-through'
60663                             },
60664                             '.de-act-link' : {
60665                                 'color' : 'red',
60666                                 'text-decoration' : 'line-through'
60667                             },
60668                             '.course-timesheet .course-highlight' : {
60669                                 'border-top-style': 'dashed !important',
60670                                 'border-bottom-bottom': 'dashed !important'
60671                             },
60672                             '.course-timesheet .course-item' : {
60673                                 'font-family'   : 'tahoma, arial, helvetica',
60674                                 'font-size'     : '11px',
60675                                 'overflow'      : 'hidden',
60676                                 'padding-left'  : '10px',
60677                                 'padding-right' : '10px',
60678                                 'padding-top' : '10px' 
60679                             }
60680                             
60681                         }, Roo.id());
60682                                 this.ds.load({});
60683                     }
60684                 },
60685                 autoWidth : true,
60686                 monitorWindowResize : false,
60687                 cellrenderer : function(v,x,r)
60688                 {
60689                     return v;
60690                 },
60691                 sm : {
60692                     xtype: 'CellSelectionModel',
60693                     xns: Roo.grid
60694                 },
60695                 dataSource : {
60696                     xtype: 'Store',
60697                     xns: Roo.data,
60698                     listeners : {
60699                         beforeload : function (_self, options)
60700                         {
60701                             options.params = options.params || {};
60702                             options.params._month = _this.monthField.getValue();
60703                             options.params.limit = 9999;
60704                             options.params['sort'] = 'when_dt';    
60705                             options.params['dir'] = 'ASC';    
60706                             this.proxy.loadResponse = this.loadResponse;
60707                             Roo.log("load?");
60708                             //this.addColumns();
60709                         },
60710                         load : function (_self, records, options)
60711                         {
60712                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
60713                                 // if you click on the translation.. you can edit it...
60714                                 var el = Roo.get(this);
60715                                 var id = el.dom.getAttribute('data-id');
60716                                 var d = el.dom.getAttribute('data-date');
60717                                 var t = el.dom.getAttribute('data-time');
60718                                 //var id = this.child('span').dom.textContent;
60719                                 
60720                                 //Roo.log(this);
60721                                 Pman.Dialog.CourseCalendar.show({
60722                                     id : id,
60723                                     when_d : d,
60724                                     when_t : t,
60725                                     productitem_active : id ? 1 : 0
60726                                 }, function() {
60727                                     _this.grid.ds.load({});
60728                                 });
60729                            
60730                            });
60731                            
60732                            _this.panel.fireEvent('resize', [ '', '' ]);
60733                         }
60734                     },
60735                     loadResponse : function(o, success, response){
60736                             // this is overridden on before load..
60737                             
60738                             Roo.log("our code?");       
60739                             //Roo.log(success);
60740                             //Roo.log(response)
60741                             delete this.activeRequest;
60742                             if(!success){
60743                                 this.fireEvent("loadexception", this, o, response);
60744                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60745                                 return;
60746                             }
60747                             var result;
60748                             try {
60749                                 result = o.reader.read(response);
60750                             }catch(e){
60751                                 Roo.log("load exception?");
60752                                 this.fireEvent("loadexception", this, o, response, e);
60753                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
60754                                 return;
60755                             }
60756                             Roo.log("ready...");        
60757                             // loop through result.records;
60758                             // and set this.tdate[date] = [] << array of records..
60759                             _this.tdata  = {};
60760                             Roo.each(result.records, function(r){
60761                                 //Roo.log(r.data);
60762                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
60763                                     _this.tdata[r.data.when_dt.format('j')] = [];
60764                                 }
60765                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
60766                             });
60767                             
60768                             //Roo.log(_this.tdata);
60769                             
60770                             result.records = [];
60771                             result.totalRecords = 6;
60772                     
60773                             // let's generate some duumy records for the rows.
60774                             //var st = _this.dateField.getValue();
60775                             
60776                             // work out monday..
60777                             //st = st.add(Date.DAY, -1 * st.format('w'));
60778                             
60779                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60780                             
60781                             var firstOfMonth = date.getFirstDayOfMonth();
60782                             var days = date.getDaysInMonth();
60783                             var d = 1;
60784                             var firstAdded = false;
60785                             for (var i = 0; i < result.totalRecords ; i++) {
60786                                 //var d= st.add(Date.DAY, i);
60787                                 var row = {};
60788                                 var added = 0;
60789                                 for(var w = 0 ; w < 7 ; w++){
60790                                     if(!firstAdded && firstOfMonth != w){
60791                                         continue;
60792                                     }
60793                                     if(d > days){
60794                                         continue;
60795                                     }
60796                                     firstAdded = true;
60797                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
60798                                     row['weekday'+w] = String.format(
60799                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
60800                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
60801                                                     d,
60802                                                     date.format('Y-m-')+dd
60803                                                 );
60804                                     added++;
60805                                     if(typeof(_this.tdata[d]) != 'undefined'){
60806                                         Roo.each(_this.tdata[d], function(r){
60807                                             var is_sub = '';
60808                                             var deactive = '';
60809                                             var id = r.id;
60810                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
60811                                             if(r.parent_id*1>0){
60812                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
60813                                                 id = r.parent_id;
60814                                             }
60815                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
60816                                                 deactive = 'de-act-link';
60817                                             }
60818                                             
60819                                             row['weekday'+w] += String.format(
60820                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
60821                                                     id, //0
60822                                                     r.product_id_name, //1
60823                                                     r.when_dt.format('h:ia'), //2
60824                                                     is_sub, //3
60825                                                     deactive, //4
60826                                                     desc // 5
60827                                             );
60828                                         });
60829                                     }
60830                                     d++;
60831                                 }
60832                                 
60833                                 // only do this if something added..
60834                                 if(added > 0){ 
60835                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
60836                                 }
60837                                 
60838                                 
60839                                 // push it twice. (second one with an hour..
60840                                 
60841                             }
60842                             //Roo.log(result);
60843                             this.fireEvent("load", this, o, o.request.arg);
60844                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
60845                         },
60846                     sortInfo : {field: 'when_dt', direction : 'ASC' },
60847                     proxy : {
60848                         xtype: 'HttpProxy',
60849                         xns: Roo.data,
60850                         method : 'GET',
60851                         url : baseURL + '/Roo/Shop_course.php'
60852                     },
60853                     reader : {
60854                         xtype: 'JsonReader',
60855                         xns: Roo.data,
60856                         id : 'id',
60857                         fields : [
60858                             {
60859                                 'name': 'id',
60860                                 'type': 'int'
60861                             },
60862                             {
60863                                 'name': 'when_dt',
60864                                 'type': 'string'
60865                             },
60866                             {
60867                                 'name': 'end_dt',
60868                                 'type': 'string'
60869                             },
60870                             {
60871                                 'name': 'parent_id',
60872                                 'type': 'int'
60873                             },
60874                             {
60875                                 'name': 'product_id',
60876                                 'type': 'int'
60877                             },
60878                             {
60879                                 'name': 'productitem_id',
60880                                 'type': 'int'
60881                             },
60882                             {
60883                                 'name': 'guid',
60884                                 'type': 'int'
60885                             }
60886                         ]
60887                     }
60888                 },
60889                 toolbar : {
60890                     xtype: 'Toolbar',
60891                     xns: Roo,
60892                     items : [
60893                         {
60894                             xtype: 'Button',
60895                             xns: Roo.Toolbar,
60896                             listeners : {
60897                                 click : function (_self, e)
60898                                 {
60899                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60900                                     sd.setMonth(sd.getMonth()-1);
60901                                     _this.monthField.setValue(sd.format('Y-m-d'));
60902                                     _this.grid.ds.load({});
60903                                 }
60904                             },
60905                             text : "Back"
60906                         },
60907                         {
60908                             xtype: 'Separator',
60909                             xns: Roo.Toolbar
60910                         },
60911                         {
60912                             xtype: 'MonthField',
60913                             xns: Roo.form,
60914                             listeners : {
60915                                 render : function (_self)
60916                                 {
60917                                     _this.monthField = _self;
60918                                    // _this.monthField.set  today
60919                                 },
60920                                 select : function (combo, date)
60921                                 {
60922                                     _this.grid.ds.load({});
60923                                 }
60924                             },
60925                             value : (function() { return new Date(); })()
60926                         },
60927                         {
60928                             xtype: 'Separator',
60929                             xns: Roo.Toolbar
60930                         },
60931                         {
60932                             xtype: 'TextItem',
60933                             xns: Roo.Toolbar,
60934                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
60935                         },
60936                         {
60937                             xtype: 'Fill',
60938                             xns: Roo.Toolbar
60939                         },
60940                         {
60941                             xtype: 'Button',
60942                             xns: Roo.Toolbar,
60943                             listeners : {
60944                                 click : function (_self, e)
60945                                 {
60946                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
60947                                     sd.setMonth(sd.getMonth()+1);
60948                                     _this.monthField.setValue(sd.format('Y-m-d'));
60949                                     _this.grid.ds.load({});
60950                                 }
60951                             },
60952                             text : "Next"
60953                         }
60954                     ]
60955                 },
60956                  
60957             }
60958         };
60959         
60960         *//*
60961  * Based on:
60962  * Ext JS Library 1.1.1
60963  * Copyright(c) 2006-2007, Ext JS, LLC.
60964  *
60965  * Originally Released Under LGPL - original licence link has changed is not relivant.
60966  *
60967  * Fork - LGPL
60968  * <script type="text/javascript">
60969  */
60970  
60971 /**
60972  * @class Roo.LoadMask
60973  * A simple utility class for generically masking elements while loading data.  If the element being masked has
60974  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
60975  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
60976  * element's UpdateManager load indicator and will be destroyed after the initial load.
60977  * @constructor
60978  * Create a new LoadMask
60979  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
60980  * @param {Object} config The config object
60981  */
60982 Roo.LoadMask = function(el, config){
60983     this.el = Roo.get(el);
60984     Roo.apply(this, config);
60985     if(this.store){
60986         this.store.on('beforeload', this.onBeforeLoad, this);
60987         this.store.on('load', this.onLoad, this);
60988         this.store.on('loadexception', this.onLoadException, this);
60989         this.removeMask = false;
60990     }else{
60991         var um = this.el.getUpdateManager();
60992         um.showLoadIndicator = false; // disable the default indicator
60993         um.on('beforeupdate', this.onBeforeLoad, this);
60994         um.on('update', this.onLoad, this);
60995         um.on('failure', this.onLoad, this);
60996         this.removeMask = true;
60997     }
60998 };
60999
61000 Roo.LoadMask.prototype = {
61001     /**
61002      * @cfg {Boolean} removeMask
61003      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61004      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61005      */
61006     /**
61007      * @cfg {String} msg
61008      * The text to display in a centered loading message box (defaults to 'Loading...')
61009      */
61010     msg : 'Loading...',
61011     /**
61012      * @cfg {String} msgCls
61013      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61014      */
61015     msgCls : 'x-mask-loading',
61016
61017     /**
61018      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61019      * @type Boolean
61020      */
61021     disabled: false,
61022
61023     /**
61024      * Disables the mask to prevent it from being displayed
61025      */
61026     disable : function(){
61027        this.disabled = true;
61028     },
61029
61030     /**
61031      * Enables the mask so that it can be displayed
61032      */
61033     enable : function(){
61034         this.disabled = false;
61035     },
61036     
61037     onLoadException : function()
61038     {
61039         Roo.log(arguments);
61040         
61041         if (typeof(arguments[3]) != 'undefined') {
61042             Roo.MessageBox.alert("Error loading",arguments[3]);
61043         } 
61044         /*
61045         try {
61046             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61047                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61048             }   
61049         } catch(e) {
61050             
61051         }
61052         */
61053     
61054         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61055     },
61056     // private
61057     onLoad : function()
61058     {
61059         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61060     },
61061
61062     // private
61063     onBeforeLoad : function(){
61064         if(!this.disabled){
61065             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61066         }
61067     },
61068
61069     // private
61070     destroy : function(){
61071         if(this.store){
61072             this.store.un('beforeload', this.onBeforeLoad, this);
61073             this.store.un('load', this.onLoad, this);
61074             this.store.un('loadexception', this.onLoadException, this);
61075         }else{
61076             var um = this.el.getUpdateManager();
61077             um.un('beforeupdate', this.onBeforeLoad, this);
61078             um.un('update', this.onLoad, this);
61079             um.un('failure', this.onLoad, this);
61080         }
61081     }
61082 };/*
61083  * Based on:
61084  * Ext JS Library 1.1.1
61085  * Copyright(c) 2006-2007, Ext JS, LLC.
61086  *
61087  * Originally Released Under LGPL - original licence link has changed is not relivant.
61088  *
61089  * Fork - LGPL
61090  * <script type="text/javascript">
61091  */
61092
61093
61094 /**
61095  * @class Roo.XTemplate
61096  * @extends Roo.Template
61097  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61098 <pre><code>
61099 var t = new Roo.XTemplate(
61100         '&lt;select name="{name}"&gt;',
61101                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61102         '&lt;/select&gt;'
61103 );
61104  
61105 // then append, applying the master template values
61106  </code></pre>
61107  *
61108  * Supported features:
61109  *
61110  *  Tags:
61111
61112 <pre><code>
61113       {a_variable} - output encoded.
61114       {a_variable.format:("Y-m-d")} - call a method on the variable
61115       {a_variable:raw} - unencoded output
61116       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61117       {a_variable:this.method_on_template(...)} - call a method on the template object.
61118  
61119 </code></pre>
61120  *  The tpl tag:
61121 <pre><code>
61122         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61123         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61124         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61125         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61126   
61127         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61128         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61129 </code></pre>
61130  *      
61131  */
61132 Roo.XTemplate = function()
61133 {
61134     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61135     if (this.html) {
61136         this.compile();
61137     }
61138 };
61139
61140
61141 Roo.extend(Roo.XTemplate, Roo.Template, {
61142
61143     /**
61144      * The various sub templates
61145      */
61146     tpls : false,
61147     /**
61148      *
61149      * basic tag replacing syntax
61150      * WORD:WORD()
61151      *
61152      * // you can fake an object call by doing this
61153      *  x.t:(test,tesT) 
61154      * 
61155      */
61156     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61157
61158     /**
61159      * compile the template
61160      *
61161      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61162      *
61163      */
61164     compile: function()
61165     {
61166         var s = this.html;
61167      
61168         s = ['<tpl>', s, '</tpl>'].join('');
61169     
61170         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61171             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61172             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61173             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61174             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61175             m,
61176             id     = 0,
61177             tpls   = [];
61178     
61179         while(true == !!(m = s.match(re))){
61180             var forMatch   = m[0].match(nameRe),
61181                 ifMatch   = m[0].match(ifRe),
61182                 execMatch   = m[0].match(execRe),
61183                 namedMatch   = m[0].match(namedRe),
61184                 
61185                 exp  = null, 
61186                 fn   = null,
61187                 exec = null,
61188                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61189                 
61190             if (ifMatch) {
61191                 // if - puts fn into test..
61192                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61193                 if(exp){
61194                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61195                 }
61196             }
61197             
61198             if (execMatch) {
61199                 // exec - calls a function... returns empty if true is  returned.
61200                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61201                 if(exp){
61202                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61203                 }
61204             }
61205             
61206             
61207             if (name) {
61208                 // for = 
61209                 switch(name){
61210                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61211                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61212                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61213                 }
61214             }
61215             var uid = namedMatch ? namedMatch[1] : id;
61216             
61217             
61218             tpls.push({
61219                 id:     namedMatch ? namedMatch[1] : id,
61220                 target: name,
61221                 exec:   exec,
61222                 test:   fn,
61223                 body:   m[1] || ''
61224             });
61225             if (namedMatch) {
61226                 s = s.replace(m[0], '');
61227             } else { 
61228                 s = s.replace(m[0], '{xtpl'+ id + '}');
61229             }
61230             ++id;
61231         }
61232         this.tpls = [];
61233         for(var i = tpls.length-1; i >= 0; --i){
61234             this.compileTpl(tpls[i]);
61235             this.tpls[tpls[i].id] = tpls[i];
61236         }
61237         this.master = tpls[tpls.length-1];
61238         return this;
61239     },
61240     /**
61241      * same as applyTemplate, except it's done to one of the subTemplates
61242      * when using named templates, you can do:
61243      *
61244      * var str = pl.applySubTemplate('your-name', values);
61245      *
61246      * 
61247      * @param {Number} id of the template
61248      * @param {Object} values to apply to template
61249      * @param {Object} parent (normaly the instance of this object)
61250      */
61251     applySubTemplate : function(id, values, parent)
61252     {
61253         
61254         
61255         var t = this.tpls[id];
61256         
61257         
61258         try { 
61259             if(t.test && !t.test.call(this, values, parent)){
61260                 return '';
61261             }
61262         } catch(e) {
61263             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61264             Roo.log(e.toString());
61265             Roo.log(t.test);
61266             return ''
61267         }
61268         try { 
61269             
61270             if(t.exec && t.exec.call(this, values, parent)){
61271                 return '';
61272             }
61273         } catch(e) {
61274             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61275             Roo.log(e.toString());
61276             Roo.log(t.exec);
61277             return ''
61278         }
61279         try {
61280             var vs = t.target ? t.target.call(this, values, parent) : values;
61281             parent = t.target ? values : parent;
61282             if(t.target && vs instanceof Array){
61283                 var buf = [];
61284                 for(var i = 0, len = vs.length; i < len; i++){
61285                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61286                 }
61287                 return buf.join('');
61288             }
61289             return t.compiled.call(this, vs, parent);
61290         } catch (e) {
61291             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61292             Roo.log(e.toString());
61293             Roo.log(t.compiled);
61294             return '';
61295         }
61296     },
61297
61298     compileTpl : function(tpl)
61299     {
61300         var fm = Roo.util.Format;
61301         var useF = this.disableFormats !== true;
61302         var sep = Roo.isGecko ? "+" : ",";
61303         var undef = function(str) {
61304             Roo.log("Property not found :"  + str);
61305             return '';
61306         };
61307         
61308         var fn = function(m, name, format, args)
61309         {
61310             //Roo.log(arguments);
61311             args = args ? args.replace(/\\'/g,"'") : args;
61312             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61313             if (typeof(format) == 'undefined') {
61314                 format= 'htmlEncode';
61315             }
61316             if (format == 'raw' ) {
61317                 format = false;
61318             }
61319             
61320             if(name.substr(0, 4) == 'xtpl'){
61321                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61322             }
61323             
61324             // build an array of options to determine if value is undefined..
61325             
61326             // basically get 'xxxx.yyyy' then do
61327             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61328             //    (function () { Roo.log("Property not found"); return ''; })() :
61329             //    ......
61330             
61331             var udef_ar = [];
61332             var lookfor = '';
61333             Roo.each(name.split('.'), function(st) {
61334                 lookfor += (lookfor.length ? '.': '') + st;
61335                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61336             });
61337             
61338             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61339             
61340             
61341             if(format && useF){
61342                 
61343                 args = args ? ',' + args : "";
61344                  
61345                 if(format.substr(0, 5) != "this."){
61346                     format = "fm." + format + '(';
61347                 }else{
61348                     format = 'this.call("'+ format.substr(5) + '", ';
61349                     args = ", values";
61350                 }
61351                 
61352                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61353             }
61354              
61355             if (args.length) {
61356                 // called with xxyx.yuu:(test,test)
61357                 // change to ()
61358                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61359             }
61360             // raw.. - :raw modifier..
61361             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61362             
61363         };
61364         var body;
61365         // branched to use + in gecko and [].join() in others
61366         if(Roo.isGecko){
61367             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61368                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61369                     "';};};";
61370         }else{
61371             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61372             body.push(tpl.body.replace(/(\r\n|\n)/g,
61373                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61374             body.push("'].join('');};};");
61375             body = body.join('');
61376         }
61377         
61378         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61379        
61380         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61381         eval(body);
61382         
61383         return this;
61384     },
61385
61386     applyTemplate : function(values){
61387         return this.master.compiled.call(this, values, {});
61388         //var s = this.subs;
61389     },
61390
61391     apply : function(){
61392         return this.applyTemplate.apply(this, arguments);
61393     }
61394
61395  });
61396
61397 Roo.XTemplate.from = function(el){
61398     el = Roo.getDom(el);
61399     return new Roo.XTemplate(el.value || el.innerHTML);
61400 };