fix dialog nested layout issues
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         }
672         
673     });
674
675
676 })();
677
678 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
679                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
680                 "Roo.app", "Roo.ux",
681                 "Roo.bootstrap",
682                 "Roo.bootstrap.dash");
683 /*
684  * Based on:
685  * Ext JS Library 1.1.1
686  * Copyright(c) 2006-2007, Ext JS, LLC.
687  *
688  * Originally Released Under LGPL - original licence link has changed is not relivant.
689  *
690  * Fork - LGPL
691  * <script type="text/javascript">
692  */
693
694 (function() {    
695     // wrappedn so fnCleanup is not in global scope...
696     if(Roo.isIE) {
697         function fnCleanUp() {
698             var p = Function.prototype;
699             delete p.createSequence;
700             delete p.defer;
701             delete p.createDelegate;
702             delete p.createCallback;
703             delete p.createInterceptor;
704
705             window.detachEvent("onunload", fnCleanUp);
706         }
707         window.attachEvent("onunload", fnCleanUp);
708     }
709 })();
710
711
712 /**
713  * @class Function
714  * These functions are available on every Function object (any JavaScript function).
715  */
716 Roo.apply(Function.prototype, {
717      /**
718      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
719      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
720      * Will create a function that is bound to those 2 args.
721      * @return {Function} The new function
722     */
723     createCallback : function(/*args...*/){
724         // make args available, in function below
725         var args = arguments;
726         var method = this;
727         return function() {
728             return method.apply(window, args);
729         };
730     },
731
732     /**
733      * Creates a delegate (callback) that sets the scope to obj.
734      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
735      * Will create a function that is automatically scoped to this.
736      * @param {Object} obj (optional) The object for which the scope is set
737      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
738      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
739      *                                             if a number the args are inserted at the specified position
740      * @return {Function} The new function
741      */
742     createDelegate : function(obj, args, appendArgs){
743         var method = this;
744         return function() {
745             var callArgs = args || arguments;
746             if(appendArgs === true){
747                 callArgs = Array.prototype.slice.call(arguments, 0);
748                 callArgs = callArgs.concat(args);
749             }else if(typeof appendArgs == "number"){
750                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
751                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
752                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
753             }
754             return method.apply(obj || window, callArgs);
755         };
756     },
757
758     /**
759      * Calls this function after the number of millseconds specified.
760      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
761      * @param {Object} obj (optional) The object for which the scope is set
762      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
763      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
764      *                                             if a number the args are inserted at the specified position
765      * @return {Number} The timeout id that can be used with clearTimeout
766      */
767     defer : function(millis, obj, args, appendArgs){
768         var fn = this.createDelegate(obj, args, appendArgs);
769         if(millis){
770             return setTimeout(fn, millis);
771         }
772         fn();
773         return 0;
774     },
775     /**
776      * Create a combined function call sequence of the original function + the passed function.
777      * The resulting function returns the results of the original function.
778      * The passed fcn is called with the parameters of the original function
779      * @param {Function} fcn The function to sequence
780      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
781      * @return {Function} The new function
782      */
783     createSequence : function(fcn, scope){
784         if(typeof fcn != "function"){
785             return this;
786         }
787         var method = this;
788         return function() {
789             var retval = method.apply(this || window, arguments);
790             fcn.apply(scope || this || window, arguments);
791             return retval;
792         };
793     },
794
795     /**
796      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
797      * The resulting function returns the results of the original function.
798      * The passed fcn is called with the parameters of the original function.
799      * @addon
800      * @param {Function} fcn The function to call before the original
801      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
802      * @return {Function} The new function
803      */
804     createInterceptor : function(fcn, scope){
805         if(typeof fcn != "function"){
806             return this;
807         }
808         var method = this;
809         return function() {
810             fcn.target = this;
811             fcn.method = method;
812             if(fcn.apply(scope || this || window, arguments) === false){
813                 return;
814             }
815             return method.apply(this || window, arguments);
816         };
817     }
818 });
819 /*
820  * Based on:
821  * Ext JS Library 1.1.1
822  * Copyright(c) 2006-2007, Ext JS, LLC.
823  *
824  * Originally Released Under LGPL - original licence link has changed is not relivant.
825  *
826  * Fork - LGPL
827  * <script type="text/javascript">
828  */
829
830 Roo.applyIf(String, {
831     
832     /** @scope String */
833     
834     /**
835      * Escapes the passed string for ' and \
836      * @param {String} string The string to escape
837      * @return {String} The escaped string
838      * @static
839      */
840     escape : function(string) {
841         return string.replace(/('|\\)/g, "\\$1");
842     },
843
844     /**
845      * Pads the left side of a string with a specified character.  This is especially useful
846      * for normalizing number and date strings.  Example usage:
847      * <pre><code>
848 var s = String.leftPad('123', 5, '0');
849 // s now contains the string: '00123'
850 </code></pre>
851      * @param {String} string The original string
852      * @param {Number} size The total length of the output string
853      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
854      * @return {String} The padded string
855      * @static
856      */
857     leftPad : function (val, size, ch) {
858         var result = new String(val);
859         if(ch === null || ch === undefined || ch === '') {
860             ch = " ";
861         }
862         while (result.length < size) {
863             result = ch + result;
864         }
865         return result;
866     },
867
868     /**
869      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
870      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
871      * <pre><code>
872 var cls = 'my-class', text = 'Some text';
873 var s = String.format('<div class="{0}">{1}</div>', cls, text);
874 // s now contains the string: '<div class="my-class">Some text</div>'
875 </code></pre>
876      * @param {String} string The tokenized string to be formatted
877      * @param {String} value1 The value to replace token {0}
878      * @param {String} value2 Etc...
879      * @return {String} The formatted string
880      * @static
881      */
882     format : function(format){
883         var args = Array.prototype.slice.call(arguments, 1);
884         return format.replace(/\{(\d+)\}/g, function(m, i){
885             return Roo.util.Format.htmlEncode(args[i]);
886         });
887     }
888   
889     
890 });
891
892 /**
893  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
894  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
895  * they are already different, the first value passed in is returned.  Note that this method returns the new value
896  * but does not change the current string.
897  * <pre><code>
898 // alternate sort directions
899 sort = sort.toggle('ASC', 'DESC');
900
901 // instead of conditional logic:
902 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
903 </code></pre>
904  * @param {String} value The value to compare to the current string
905  * @param {String} other The new value to use if the string already equals the first value passed in
906  * @return {String} The new value
907  */
908  
909 String.prototype.toggle = function(value, other){
910     return this == value ? other : value;
911 };
912
913
914 /**
915   * Remove invalid unicode characters from a string 
916   *
917   * @return {String} The clean string
918   */
919 String.prototype.unicodeClean = function () {
920     return this.replace(/[\s\S]/g,
921         function(character) {
922             if (character.charCodeAt()< 256) {
923               return character;
924            }
925            try {
926                 encodeURIComponent(character);
927            } catch(e) { 
928               return '';
929            }
930            return character;
931         }
932     );
933 };
934   
935 /*
936  * Based on:
937  * Ext JS Library 1.1.1
938  * Copyright(c) 2006-2007, Ext JS, LLC.
939  *
940  * Originally Released Under LGPL - original licence link has changed is not relivant.
941  *
942  * Fork - LGPL
943  * <script type="text/javascript">
944  */
945
946  /**
947  * @class Number
948  */
949 Roo.applyIf(Number.prototype, {
950     /**
951      * Checks whether or not the current number is within a desired range.  If the number is already within the
952      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
953      * exceeded.  Note that this method returns the constrained value but does not change the current number.
954      * @param {Number} min The minimum number in the range
955      * @param {Number} max The maximum number in the range
956      * @return {Number} The constrained value if outside the range, otherwise the current value
957      */
958     constrain : function(min, max){
959         return Math.min(Math.max(this, min), max);
960     }
961 });/*
962  * Based on:
963  * Ext JS Library 1.1.1
964  * Copyright(c) 2006-2007, Ext JS, LLC.
965  *
966  * Originally Released Under LGPL - original licence link has changed is not relivant.
967  *
968  * Fork - LGPL
969  * <script type="text/javascript">
970  */
971  /**
972  * @class Array
973  */
974 Roo.applyIf(Array.prototype, {
975     /**
976      * 
977      * Checks whether or not the specified object exists in the array.
978      * @param {Object} o The object to check for
979      * @return {Number} The index of o in the array (or -1 if it is not found)
980      */
981     indexOf : function(o){
982        for (var i = 0, len = this.length; i < len; i++){
983               if(this[i] == o) { return i; }
984        }
985            return -1;
986     },
987
988     /**
989      * Removes the specified object from the array.  If the object is not found nothing happens.
990      * @param {Object} o The object to remove
991      */
992     remove : function(o){
993        var index = this.indexOf(o);
994        if(index != -1){
995            this.splice(index, 1);
996        }
997     },
998     /**
999      * Map (JS 1.6 compatibility)
1000      * @param {Function} function  to call
1001      */
1002     map : function(fun )
1003     {
1004         var len = this.length >>> 0;
1005         if (typeof fun != "function") {
1006             throw new TypeError();
1007         }
1008         var res = new Array(len);
1009         var thisp = arguments[1];
1010         for (var i = 0; i < len; i++)
1011         {
1012             if (i in this) {
1013                 res[i] = fun.call(thisp, this[i], i, this);
1014             }
1015         }
1016
1017         return res;
1018     },
1019     /**
1020      * equals
1021      * @param {Array} o The array to compare to
1022      * @returns {Boolean} true if the same
1023      */
1024     equals : function(b)
1025     {
1026         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1027         if (this === b) {
1028             return true;
1029          }
1030         if (b == null) {
1031             return false;
1032         }
1033         if (this.length !== b.length) {
1034             return false;
1035         }
1036       
1037         // sort?? a.sort().equals(b.sort());
1038       
1039         for (var i = 0; i < this.length; ++i) {
1040             if (this[i] !== b[i]) {
1041                 return false;
1042             }
1043         }
1044         return true;
1045     }
1046 });
1047
1048
1049  
1050 /*
1051  * Based on:
1052  * Ext JS Library 1.1.1
1053  * Copyright(c) 2006-2007, Ext JS, LLC.
1054  *
1055  * Originally Released Under LGPL - original licence link has changed is not relivant.
1056  *
1057  * Fork - LGPL
1058  * <script type="text/javascript">
1059  */
1060
1061 /**
1062  * @class Date
1063  *
1064  * The date parsing and format syntax is a subset of
1065  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1066  * supported will provide results equivalent to their PHP versions.
1067  *
1068  * Following is the list of all currently supported formats:
1069  *<pre>
1070 Sample date:
1071 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1072
1073 Format  Output      Description
1074 ------  ----------  --------------------------------------------------------------
1075   d      10         Day of the month, 2 digits with leading zeros
1076   D      Wed        A textual representation of a day, three letters
1077   j      10         Day of the month without leading zeros
1078   l      Wednesday  A full textual representation of the day of the week
1079   S      th         English ordinal day of month suffix, 2 chars (use with j)
1080   w      3          Numeric representation of the day of the week
1081   z      9          The julian date, or day of the year (0-365)
1082   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1083   F      January    A full textual representation of the month
1084   m      01         Numeric representation of a month, with leading zeros
1085   M      Jan        Month name abbreviation, three letters
1086   n      1          Numeric representation of a month, without leading zeros
1087   t      31         Number of days in the given month
1088   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1089   Y      2007       A full numeric representation of a year, 4 digits
1090   y      07         A two digit representation of a year
1091   a      pm         Lowercase Ante meridiem and Post meridiem
1092   A      PM         Uppercase Ante meridiem and Post meridiem
1093   g      3          12-hour format of an hour without leading zeros
1094   G      15         24-hour format of an hour without leading zeros
1095   h      03         12-hour format of an hour with leading zeros
1096   H      15         24-hour format of an hour with leading zeros
1097   i      05         Minutes with leading zeros
1098   s      01         Seconds, with leading zeros
1099   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1100   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1101   T      CST        Timezone setting of the machine running the code
1102   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1103 </pre>
1104  *
1105  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1106  * <pre><code>
1107 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1108 document.write(dt.format('Y-m-d'));                         //2007-01-10
1109 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1110 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1111  </code></pre>
1112  *
1113  * Here are some standard date/time patterns that you might find helpful.  They
1114  * are not part of the source of Date.js, but to use them you can simply copy this
1115  * block of code into any script that is included after Date.js and they will also become
1116  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1117  * <pre><code>
1118 Date.patterns = {
1119     ISO8601Long:"Y-m-d H:i:s",
1120     ISO8601Short:"Y-m-d",
1121     ShortDate: "n/j/Y",
1122     LongDate: "l, F d, Y",
1123     FullDateTime: "l, F d, Y g:i:s A",
1124     MonthDay: "F d",
1125     ShortTime: "g:i A",
1126     LongTime: "g:i:s A",
1127     SortableDateTime: "Y-m-d\\TH:i:s",
1128     UniversalSortableDateTime: "Y-m-d H:i:sO",
1129     YearMonth: "F, Y"
1130 };
1131 </code></pre>
1132  *
1133  * Example usage:
1134  * <pre><code>
1135 var dt = new Date();
1136 document.write(dt.format(Date.patterns.ShortDate));
1137  </code></pre>
1138  */
1139
1140 /*
1141  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1142  * They generate precompiled functions from date formats instead of parsing and
1143  * processing the pattern every time you format a date.  These functions are available
1144  * on every Date object (any javascript function).
1145  *
1146  * The original article and download are here:
1147  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1148  *
1149  */
1150  
1151  
1152  // was in core
1153 /**
1154  Returns the number of milliseconds between this date and date
1155  @param {Date} date (optional) Defaults to now
1156  @return {Number} The diff in milliseconds
1157  @member Date getElapsed
1158  */
1159 Date.prototype.getElapsed = function(date) {
1160         return Math.abs((date || new Date()).getTime()-this.getTime());
1161 };
1162 // was in date file..
1163
1164
1165 // private
1166 Date.parseFunctions = {count:0};
1167 // private
1168 Date.parseRegexes = [];
1169 // private
1170 Date.formatFunctions = {count:0};
1171
1172 // private
1173 Date.prototype.dateFormat = function(format) {
1174     if (Date.formatFunctions[format] == null) {
1175         Date.createNewFormat(format);
1176     }
1177     var func = Date.formatFunctions[format];
1178     return this[func]();
1179 };
1180
1181
1182 /**
1183  * Formats a date given the supplied format string
1184  * @param {String} format The format string
1185  * @return {String} The formatted date
1186  * @method
1187  */
1188 Date.prototype.format = Date.prototype.dateFormat;
1189
1190 // private
1191 Date.createNewFormat = function(format) {
1192     var funcName = "format" + Date.formatFunctions.count++;
1193     Date.formatFunctions[format] = funcName;
1194     var code = "Date.prototype." + funcName + " = function(){return ";
1195     var special = false;
1196     var ch = '';
1197     for (var i = 0; i < format.length; ++i) {
1198         ch = format.charAt(i);
1199         if (!special && ch == "\\") {
1200             special = true;
1201         }
1202         else if (special) {
1203             special = false;
1204             code += "'" + String.escape(ch) + "' + ";
1205         }
1206         else {
1207             code += Date.getFormatCode(ch);
1208         }
1209     }
1210     /** eval:var:zzzzzzzzzzzzz */
1211     eval(code.substring(0, code.length - 3) + ";}");
1212 };
1213
1214 // private
1215 Date.getFormatCode = function(character) {
1216     switch (character) {
1217     case "d":
1218         return "String.leftPad(this.getDate(), 2, '0') + ";
1219     case "D":
1220         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1221     case "j":
1222         return "this.getDate() + ";
1223     case "l":
1224         return "Date.dayNames[this.getDay()] + ";
1225     case "S":
1226         return "this.getSuffix() + ";
1227     case "w":
1228         return "this.getDay() + ";
1229     case "z":
1230         return "this.getDayOfYear() + ";
1231     case "W":
1232         return "this.getWeekOfYear() + ";
1233     case "F":
1234         return "Date.monthNames[this.getMonth()] + ";
1235     case "m":
1236         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1237     case "M":
1238         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1239     case "n":
1240         return "(this.getMonth() + 1) + ";
1241     case "t":
1242         return "this.getDaysInMonth() + ";
1243     case "L":
1244         return "(this.isLeapYear() ? 1 : 0) + ";
1245     case "Y":
1246         return "this.getFullYear() + ";
1247     case "y":
1248         return "('' + this.getFullYear()).substring(2, 4) + ";
1249     case "a":
1250         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1251     case "A":
1252         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1253     case "g":
1254         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1255     case "G":
1256         return "this.getHours() + ";
1257     case "h":
1258         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1259     case "H":
1260         return "String.leftPad(this.getHours(), 2, '0') + ";
1261     case "i":
1262         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1263     case "s":
1264         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1265     case "O":
1266         return "this.getGMTOffset() + ";
1267     case "P":
1268         return "this.getGMTColonOffset() + ";
1269     case "T":
1270         return "this.getTimezone() + ";
1271     case "Z":
1272         return "(this.getTimezoneOffset() * -60) + ";
1273     default:
1274         return "'" + String.escape(character) + "' + ";
1275     }
1276 };
1277
1278 /**
1279  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1280  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1281  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1282  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1283  * string or the parse operation will fail.
1284  * Example Usage:
1285 <pre><code>
1286 //dt = Fri May 25 2007 (current date)
1287 var dt = new Date();
1288
1289 //dt = Thu May 25 2006 (today's month/day in 2006)
1290 dt = Date.parseDate("2006", "Y");
1291
1292 //dt = Sun Jan 15 2006 (all date parts specified)
1293 dt = Date.parseDate("2006-1-15", "Y-m-d");
1294
1295 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1296 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1297 </code></pre>
1298  * @param {String} input The unparsed date as a string
1299  * @param {String} format The format the date is in
1300  * @return {Date} The parsed date
1301  * @static
1302  */
1303 Date.parseDate = function(input, format) {
1304     if (Date.parseFunctions[format] == null) {
1305         Date.createParser(format);
1306     }
1307     var func = Date.parseFunctions[format];
1308     return Date[func](input);
1309 };
1310 /**
1311  * @private
1312  */
1313
1314 Date.createParser = function(format) {
1315     var funcName = "parse" + Date.parseFunctions.count++;
1316     var regexNum = Date.parseRegexes.length;
1317     var currentGroup = 1;
1318     Date.parseFunctions[format] = funcName;
1319
1320     var code = "Date." + funcName + " = function(input){\n"
1321         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1322         + "var d = new Date();\n"
1323         + "y = d.getFullYear();\n"
1324         + "m = d.getMonth();\n"
1325         + "d = d.getDate();\n"
1326         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1327         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1328         + "if (results && results.length > 0) {";
1329     var regex = "";
1330
1331     var special = false;
1332     var ch = '';
1333     for (var i = 0; i < format.length; ++i) {
1334         ch = format.charAt(i);
1335         if (!special && ch == "\\") {
1336             special = true;
1337         }
1338         else if (special) {
1339             special = false;
1340             regex += String.escape(ch);
1341         }
1342         else {
1343             var obj = Date.formatCodeToRegex(ch, currentGroup);
1344             currentGroup += obj.g;
1345             regex += obj.s;
1346             if (obj.g && obj.c) {
1347                 code += obj.c;
1348             }
1349         }
1350     }
1351
1352     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1353         + "{v = new Date(y, m, d, h, i, s);}\n"
1354         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1355         + "{v = new Date(y, m, d, h, i);}\n"
1356         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1357         + "{v = new Date(y, m, d, h);}\n"
1358         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1359         + "{v = new Date(y, m, d);}\n"
1360         + "else if (y >= 0 && m >= 0)\n"
1361         + "{v = new Date(y, m);}\n"
1362         + "else if (y >= 0)\n"
1363         + "{v = new Date(y);}\n"
1364         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1365         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1366         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1367         + ";}";
1368
1369     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1370     /** eval:var:zzzzzzzzzzzzz */
1371     eval(code);
1372 };
1373
1374 // private
1375 Date.formatCodeToRegex = function(character, currentGroup) {
1376     switch (character) {
1377     case "D":
1378         return {g:0,
1379         c:null,
1380         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1381     case "j":
1382         return {g:1,
1383             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{1,2})"}; // day of month without leading zeroes
1385     case "d":
1386         return {g:1,
1387             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1388             s:"(\\d{2})"}; // day of month with leading zeroes
1389     case "l":
1390         return {g:0,
1391             c:null,
1392             s:"(?:" + Date.dayNames.join("|") + ")"};
1393     case "S":
1394         return {g:0,
1395             c:null,
1396             s:"(?:st|nd|rd|th)"};
1397     case "w":
1398         return {g:0,
1399             c:null,
1400             s:"\\d"};
1401     case "z":
1402         return {g:0,
1403             c:null,
1404             s:"(?:\\d{1,3})"};
1405     case "W":
1406         return {g:0,
1407             c:null,
1408             s:"(?:\\d{2})"};
1409     case "F":
1410         return {g:1,
1411             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1412             s:"(" + Date.monthNames.join("|") + ")"};
1413     case "M":
1414         return {g:1,
1415             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1416             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1417     case "n":
1418         return {g:1,
1419             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1420             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1421     case "m":
1422         return {g:1,
1423             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1424             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1425     case "t":
1426         return {g:0,
1427             c:null,
1428             s:"\\d{1,2}"};
1429     case "L":
1430         return {g:0,
1431             c:null,
1432             s:"(?:1|0)"};
1433     case "Y":
1434         return {g:1,
1435             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{4})"};
1437     case "y":
1438         return {g:1,
1439             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1440                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1441             s:"(\\d{1,2})"};
1442     case "a":
1443         return {g:1,
1444             c:"if (results[" + currentGroup + "] == 'am') {\n"
1445                 + "if (h == 12) { h = 0; }\n"
1446                 + "} else { if (h < 12) { h += 12; }}",
1447             s:"(am|pm)"};
1448     case "A":
1449         return {g:1,
1450             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1451                 + "if (h == 12) { h = 0; }\n"
1452                 + "} else { if (h < 12) { h += 12; }}",
1453             s:"(AM|PM)"};
1454     case "g":
1455     case "G":
1456         return {g:1,
1457             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1458             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1459     case "h":
1460     case "H":
1461         return {g:1,
1462             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1463             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1464     case "i":
1465         return {g:1,
1466             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1467             s:"(\\d{2})"};
1468     case "s":
1469         return {g:1,
1470             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1471             s:"(\\d{2})"};
1472     case "O":
1473         return {g:1,
1474             c:[
1475                 "o = results[", currentGroup, "];\n",
1476                 "var sn = o.substring(0,1);\n", // get + / - sign
1477                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1478                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1479                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1480                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1481             ].join(""),
1482             s:"([+\-]\\d{2,4})"};
1483     
1484     
1485     case "P":
1486         return {g:1,
1487                 c:[
1488                    "o = results[", currentGroup, "];\n",
1489                    "var sn = o.substring(0,1);\n",
1490                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1491                    "var mn = o.substring(4,6) % 60;\n",
1492                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1493                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1494             ].join(""),
1495             s:"([+\-]\\d{4})"};
1496     case "T":
1497         return {g:0,
1498             c:null,
1499             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1500     case "Z":
1501         return {g:1,
1502             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1503                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1504             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1505     default:
1506         return {g:0,
1507             c:null,
1508             s:String.escape(character)};
1509     }
1510 };
1511
1512 /**
1513  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1514  * @return {String} The abbreviated timezone name (e.g. 'CST')
1515  */
1516 Date.prototype.getTimezone = function() {
1517     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1518 };
1519
1520 /**
1521  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1522  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1523  */
1524 Date.prototype.getGMTOffset = function() {
1525     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1526         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1527         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1528 };
1529
1530 /**
1531  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1532  * @return {String} 2-characters representing hours and 2-characters representing minutes
1533  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1534  */
1535 Date.prototype.getGMTColonOffset = function() {
1536         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1537                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1538                 + ":"
1539                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1540 }
1541
1542 /**
1543  * Get the numeric day number of the year, adjusted for leap year.
1544  * @return {Number} 0 through 364 (365 in leap years)
1545  */
1546 Date.prototype.getDayOfYear = function() {
1547     var num = 0;
1548     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1549     for (var i = 0; i < this.getMonth(); ++i) {
1550         num += Date.daysInMonth[i];
1551     }
1552     return num + this.getDate() - 1;
1553 };
1554
1555 /**
1556  * Get the string representation of the numeric week number of the year
1557  * (equivalent to the format specifier 'W').
1558  * @return {String} '00' through '52'
1559  */
1560 Date.prototype.getWeekOfYear = function() {
1561     // Skip to Thursday of this week
1562     var now = this.getDayOfYear() + (4 - this.getDay());
1563     // Find the first Thursday of the year
1564     var jan1 = new Date(this.getFullYear(), 0, 1);
1565     var then = (7 - jan1.getDay() + 4);
1566     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1567 };
1568
1569 /**
1570  * Whether or not the current date is in a leap year.
1571  * @return {Boolean} True if the current date is in a leap year, else false
1572  */
1573 Date.prototype.isLeapYear = function() {
1574     var year = this.getFullYear();
1575     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1576 };
1577
1578 /**
1579  * Get the first day of the current month, adjusted for leap year.  The returned value
1580  * is the numeric day index within the week (0-6) which can be used in conjunction with
1581  * the {@link #monthNames} array to retrieve the textual day name.
1582  * Example:
1583  *<pre><code>
1584 var dt = new Date('1/10/2007');
1585 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1586 </code></pre>
1587  * @return {Number} The day number (0-6)
1588  */
1589 Date.prototype.getFirstDayOfMonth = function() {
1590     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1591     return (day < 0) ? (day + 7) : day;
1592 };
1593
1594 /**
1595  * Get the last day of the current month, adjusted for leap year.  The returned value
1596  * is the numeric day index within the week (0-6) which can be used in conjunction with
1597  * the {@link #monthNames} array to retrieve the textual day name.
1598  * Example:
1599  *<pre><code>
1600 var dt = new Date('1/10/2007');
1601 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1602 </code></pre>
1603  * @return {Number} The day number (0-6)
1604  */
1605 Date.prototype.getLastDayOfMonth = function() {
1606     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1607     return (day < 0) ? (day + 7) : day;
1608 };
1609
1610
1611 /**
1612  * Get the first date of this date's month
1613  * @return {Date}
1614  */
1615 Date.prototype.getFirstDateOfMonth = function() {
1616     return new Date(this.getFullYear(), this.getMonth(), 1);
1617 };
1618
1619 /**
1620  * Get the last date of this date's month
1621  * @return {Date}
1622  */
1623 Date.prototype.getLastDateOfMonth = function() {
1624     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1625 };
1626 /**
1627  * Get the number of days in the current month, adjusted for leap year.
1628  * @return {Number} The number of days in the month
1629  */
1630 Date.prototype.getDaysInMonth = function() {
1631     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1632     return Date.daysInMonth[this.getMonth()];
1633 };
1634
1635 /**
1636  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1637  * @return {String} 'st, 'nd', 'rd' or 'th'
1638  */
1639 Date.prototype.getSuffix = function() {
1640     switch (this.getDate()) {
1641         case 1:
1642         case 21:
1643         case 31:
1644             return "st";
1645         case 2:
1646         case 22:
1647             return "nd";
1648         case 3:
1649         case 23:
1650             return "rd";
1651         default:
1652             return "th";
1653     }
1654 };
1655
1656 // private
1657 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1658
1659 /**
1660  * An array of textual month names.
1661  * Override these values for international dates, for example...
1662  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1663  * @type Array
1664  * @static
1665  */
1666 Date.monthNames =
1667    ["January",
1668     "February",
1669     "March",
1670     "April",
1671     "May",
1672     "June",
1673     "July",
1674     "August",
1675     "September",
1676     "October",
1677     "November",
1678     "December"];
1679
1680 /**
1681  * An array of textual day names.
1682  * Override these values for international dates, for example...
1683  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1684  * @type Array
1685  * @static
1686  */
1687 Date.dayNames =
1688    ["Sunday",
1689     "Monday",
1690     "Tuesday",
1691     "Wednesday",
1692     "Thursday",
1693     "Friday",
1694     "Saturday"];
1695
1696 // private
1697 Date.y2kYear = 50;
1698 // private
1699 Date.monthNumbers = {
1700     Jan:0,
1701     Feb:1,
1702     Mar:2,
1703     Apr:3,
1704     May:4,
1705     Jun:5,
1706     Jul:6,
1707     Aug:7,
1708     Sep:8,
1709     Oct:9,
1710     Nov:10,
1711     Dec:11};
1712
1713 /**
1714  * Creates and returns a new Date instance with the exact same date value as the called instance.
1715  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1716  * variable will also be changed.  When the intention is to create a new variable that will not
1717  * modify the original instance, you should create a clone.
1718  *
1719  * Example of correctly cloning a date:
1720  * <pre><code>
1721 //wrong way:
1722 var orig = new Date('10/1/2006');
1723 var copy = orig;
1724 copy.setDate(5);
1725 document.write(orig);  //returns 'Thu Oct 05 2006'!
1726
1727 //correct way:
1728 var orig = new Date('10/1/2006');
1729 var copy = orig.clone();
1730 copy.setDate(5);
1731 document.write(orig);  //returns 'Thu Oct 01 2006'
1732 </code></pre>
1733  * @return {Date} The new Date instance
1734  */
1735 Date.prototype.clone = function() {
1736         return new Date(this.getTime());
1737 };
1738
1739 /**
1740  * Clears any time information from this date
1741  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1742  @return {Date} this or the clone
1743  */
1744 Date.prototype.clearTime = function(clone){
1745     if(clone){
1746         return this.clone().clearTime();
1747     }
1748     this.setHours(0);
1749     this.setMinutes(0);
1750     this.setSeconds(0);
1751     this.setMilliseconds(0);
1752     return this;
1753 };
1754
1755 // private
1756 // safari setMonth is broken -- check that this is only donw once...
1757 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1758     Date.brokenSetMonth = Date.prototype.setMonth;
1759         Date.prototype.setMonth = function(num){
1760                 if(num <= -1){
1761                         var n = Math.ceil(-num);
1762                         var back_year = Math.ceil(n/12);
1763                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1764                         this.setFullYear(this.getFullYear() - back_year);
1765                         return Date.brokenSetMonth.call(this, month);
1766                 } else {
1767                         return Date.brokenSetMonth.apply(this, arguments);
1768                 }
1769         };
1770 }
1771
1772 /** Date interval constant 
1773 * @static 
1774 * @type String */
1775 Date.MILLI = "ms";
1776 /** Date interval constant 
1777 * @static 
1778 * @type String */
1779 Date.SECOND = "s";
1780 /** Date interval constant 
1781 * @static 
1782 * @type String */
1783 Date.MINUTE = "mi";
1784 /** Date interval constant 
1785 * @static 
1786 * @type String */
1787 Date.HOUR = "h";
1788 /** Date interval constant 
1789 * @static 
1790 * @type String */
1791 Date.DAY = "d";
1792 /** Date interval constant 
1793 * @static 
1794 * @type String */
1795 Date.MONTH = "mo";
1796 /** Date interval constant 
1797 * @static 
1798 * @type String */
1799 Date.YEAR = "y";
1800
1801 /**
1802  * Provides a convenient method of performing basic date arithmetic.  This method
1803  * does not modify the Date instance being called - it creates and returns
1804  * a new Date instance containing the resulting date value.
1805  *
1806  * Examples:
1807  * <pre><code>
1808 //Basic usage:
1809 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1810 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1811
1812 //Negative values will subtract correctly:
1813 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1814 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1815
1816 //You can even chain several calls together in one line!
1817 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1818 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1819  </code></pre>
1820  *
1821  * @param {String} interval   A valid date interval enum value
1822  * @param {Number} value      The amount to add to the current date
1823  * @return {Date} The new Date instance
1824  */
1825 Date.prototype.add = function(interval, value){
1826   var d = this.clone();
1827   if (!interval || value === 0) { return d; }
1828   switch(interval.toLowerCase()){
1829     case Date.MILLI:
1830       d.setMilliseconds(this.getMilliseconds() + value);
1831       break;
1832     case Date.SECOND:
1833       d.setSeconds(this.getSeconds() + value);
1834       break;
1835     case Date.MINUTE:
1836       d.setMinutes(this.getMinutes() + value);
1837       break;
1838     case Date.HOUR:
1839       d.setHours(this.getHours() + value);
1840       break;
1841     case Date.DAY:
1842       d.setDate(this.getDate() + value);
1843       break;
1844     case Date.MONTH:
1845       var day = this.getDate();
1846       if(day > 28){
1847           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1848       }
1849       d.setDate(day);
1850       d.setMonth(this.getMonth() + value);
1851       break;
1852     case Date.YEAR:
1853       d.setFullYear(this.getFullYear() + value);
1854       break;
1855   }
1856   return d;
1857 };
1858 /*
1859  * Based on:
1860  * Ext JS Library 1.1.1
1861  * Copyright(c) 2006-2007, Ext JS, LLC.
1862  *
1863  * Originally Released Under LGPL - original licence link has changed is not relivant.
1864  *
1865  * Fork - LGPL
1866  * <script type="text/javascript">
1867  */
1868
1869 /**
1870  * @class Roo.lib.Dom
1871  * @static
1872  * 
1873  * Dom utils (from YIU afaik)
1874  * 
1875  **/
1876 Roo.lib.Dom = {
1877     /**
1878      * Get the view width
1879      * @param {Boolean} full True will get the full document, otherwise it's the view width
1880      * @return {Number} The width
1881      */
1882      
1883     getViewWidth : function(full) {
1884         return full ? this.getDocumentWidth() : this.getViewportWidth();
1885     },
1886     /**
1887      * Get the view height
1888      * @param {Boolean} full True will get the full document, otherwise it's the view height
1889      * @return {Number} The height
1890      */
1891     getViewHeight : function(full) {
1892         return full ? this.getDocumentHeight() : this.getViewportHeight();
1893     },
1894
1895     getDocumentHeight: function() {
1896         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1897         return Math.max(scrollHeight, this.getViewportHeight());
1898     },
1899
1900     getDocumentWidth: function() {
1901         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1902         return Math.max(scrollWidth, this.getViewportWidth());
1903     },
1904
1905     getViewportHeight: function() {
1906         var height = self.innerHeight;
1907         var mode = document.compatMode;
1908
1909         if ((mode || Roo.isIE) && !Roo.isOpera) {
1910             height = (mode == "CSS1Compat") ?
1911                      document.documentElement.clientHeight :
1912                      document.body.clientHeight;
1913         }
1914
1915         return height;
1916     },
1917
1918     getViewportWidth: function() {
1919         var width = self.innerWidth;
1920         var mode = document.compatMode;
1921
1922         if (mode || Roo.isIE) {
1923             width = (mode == "CSS1Compat") ?
1924                     document.documentElement.clientWidth :
1925                     document.body.clientWidth;
1926         }
1927         return width;
1928     },
1929
1930     isAncestor : function(p, c) {
1931         p = Roo.getDom(p);
1932         c = Roo.getDom(c);
1933         if (!p || !c) {
1934             return false;
1935         }
1936
1937         if (p.contains && !Roo.isSafari) {
1938             return p.contains(c);
1939         } else if (p.compareDocumentPosition) {
1940             return !!(p.compareDocumentPosition(c) & 16);
1941         } else {
1942             var parent = c.parentNode;
1943             while (parent) {
1944                 if (parent == p) {
1945                     return true;
1946                 }
1947                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1948                     return false;
1949                 }
1950                 parent = parent.parentNode;
1951             }
1952             return false;
1953         }
1954     },
1955
1956     getRegion : function(el) {
1957         return Roo.lib.Region.getRegion(el);
1958     },
1959
1960     getY : function(el) {
1961         return this.getXY(el)[1];
1962     },
1963
1964     getX : function(el) {
1965         return this.getXY(el)[0];
1966     },
1967
1968     getXY : function(el) {
1969         var p, pe, b, scroll, bd = document.body;
1970         el = Roo.getDom(el);
1971         var fly = Roo.lib.AnimBase.fly;
1972         if (el.getBoundingClientRect) {
1973             b = el.getBoundingClientRect();
1974             scroll = fly(document).getScroll();
1975             return [b.left + scroll.left, b.top + scroll.top];
1976         }
1977         var x = 0, y = 0;
1978
1979         p = el;
1980
1981         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1982
1983         while (p) {
1984
1985             x += p.offsetLeft;
1986             y += p.offsetTop;
1987
1988             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1989                 hasAbsolute = true;
1990             }
1991
1992             if (Roo.isGecko) {
1993                 pe = fly(p);
1994
1995                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1996                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1997
1998
1999                 x += bl;
2000                 y += bt;
2001
2002
2003                 if (p != el && pe.getStyle('overflow') != 'visible') {
2004                     x += bl;
2005                     y += bt;
2006                 }
2007             }
2008             p = p.offsetParent;
2009         }
2010
2011         if (Roo.isSafari && hasAbsolute) {
2012             x -= bd.offsetLeft;
2013             y -= bd.offsetTop;
2014         }
2015
2016         if (Roo.isGecko && !hasAbsolute) {
2017             var dbd = fly(bd);
2018             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2019             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2020         }
2021
2022         p = el.parentNode;
2023         while (p && p != bd) {
2024             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2025                 x -= p.scrollLeft;
2026                 y -= p.scrollTop;
2027             }
2028             p = p.parentNode;
2029         }
2030         return [x, y];
2031     },
2032  
2033   
2034
2035
2036     setXY : function(el, xy) {
2037         el = Roo.fly(el, '_setXY');
2038         el.position();
2039         var pts = el.translatePoints(xy);
2040         if (xy[0] !== false) {
2041             el.dom.style.left = pts.left + "px";
2042         }
2043         if (xy[1] !== false) {
2044             el.dom.style.top = pts.top + "px";
2045         }
2046     },
2047
2048     setX : function(el, x) {
2049         this.setXY(el, [x, false]);
2050     },
2051
2052     setY : function(el, y) {
2053         this.setXY(el, [false, y]);
2054     }
2055 };
2056 /*
2057  * Portions of this file are based on pieces of Yahoo User Interface Library
2058  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2059  * YUI licensed under the BSD License:
2060  * http://developer.yahoo.net/yui/license.txt
2061  * <script type="text/javascript">
2062  *
2063  */
2064
2065 Roo.lib.Event = function() {
2066     var loadComplete = false;
2067     var listeners = [];
2068     var unloadListeners = [];
2069     var retryCount = 0;
2070     var onAvailStack = [];
2071     var counter = 0;
2072     var lastError = null;
2073
2074     return {
2075         POLL_RETRYS: 200,
2076         POLL_INTERVAL: 20,
2077         EL: 0,
2078         TYPE: 1,
2079         FN: 2,
2080         WFN: 3,
2081         OBJ: 3,
2082         ADJ_SCOPE: 4,
2083         _interval: null,
2084
2085         startInterval: function() {
2086             if (!this._interval) {
2087                 var self = this;
2088                 var callback = function() {
2089                     self._tryPreloadAttach();
2090                 };
2091                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2092
2093             }
2094         },
2095
2096         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2097             onAvailStack.push({ id:         p_id,
2098                 fn:         p_fn,
2099                 obj:        p_obj,
2100                 override:   p_override,
2101                 checkReady: false    });
2102
2103             retryCount = this.POLL_RETRYS;
2104             this.startInterval();
2105         },
2106
2107
2108         addListener: function(el, eventName, fn) {
2109             el = Roo.getDom(el);
2110             if (!el || !fn) {
2111                 return false;
2112             }
2113
2114             if ("unload" == eventName) {
2115                 unloadListeners[unloadListeners.length] =
2116                 [el, eventName, fn];
2117                 return true;
2118             }
2119
2120             var wrappedFn = function(e) {
2121                 return fn(Roo.lib.Event.getEvent(e));
2122             };
2123
2124             var li = [el, eventName, fn, wrappedFn];
2125
2126             var index = listeners.length;
2127             listeners[index] = li;
2128
2129             this.doAdd(el, eventName, wrappedFn, false);
2130             return true;
2131
2132         },
2133
2134
2135         removeListener: function(el, eventName, fn) {
2136             var i, len;
2137
2138             el = Roo.getDom(el);
2139
2140             if(!fn) {
2141                 return this.purgeElement(el, false, eventName);
2142             }
2143
2144
2145             if ("unload" == eventName) {
2146
2147                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2148                     var li = unloadListeners[i];
2149                     if (li &&
2150                         li[0] == el &&
2151                         li[1] == eventName &&
2152                         li[2] == fn) {
2153                         unloadListeners.splice(i, 1);
2154                         return true;
2155                     }
2156                 }
2157
2158                 return false;
2159             }
2160
2161             var cacheItem = null;
2162
2163
2164             var index = arguments[3];
2165
2166             if ("undefined" == typeof index) {
2167                 index = this._getCacheIndex(el, eventName, fn);
2168             }
2169
2170             if (index >= 0) {
2171                 cacheItem = listeners[index];
2172             }
2173
2174             if (!el || !cacheItem) {
2175                 return false;
2176             }
2177
2178             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2179
2180             delete listeners[index][this.WFN];
2181             delete listeners[index][this.FN];
2182             listeners.splice(index, 1);
2183
2184             return true;
2185
2186         },
2187
2188
2189         getTarget: function(ev, resolveTextNode) {
2190             ev = ev.browserEvent || ev;
2191             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2192             var t = ev.target || ev.srcElement;
2193             return this.resolveTextNode(t);
2194         },
2195
2196
2197         resolveTextNode: function(node) {
2198             if (Roo.isSafari && node && 3 == node.nodeType) {
2199                 return node.parentNode;
2200             } else {
2201                 return node;
2202             }
2203         },
2204
2205
2206         getPageX: function(ev) {
2207             ev = ev.browserEvent || ev;
2208             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2209             var x = ev.pageX;
2210             if (!x && 0 !== x) {
2211                 x = ev.clientX || 0;
2212
2213                 if (Roo.isIE) {
2214                     x += this.getScroll()[1];
2215                 }
2216             }
2217
2218             return x;
2219         },
2220
2221
2222         getPageY: function(ev) {
2223             ev = ev.browserEvent || ev;
2224             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2225             var y = ev.pageY;
2226             if (!y && 0 !== y) {
2227                 y = ev.clientY || 0;
2228
2229                 if (Roo.isIE) {
2230                     y += this.getScroll()[0];
2231                 }
2232             }
2233
2234
2235             return y;
2236         },
2237
2238
2239         getXY: function(ev) {
2240             ev = ev.browserEvent || ev;
2241             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2242             return [this.getPageX(ev), this.getPageY(ev)];
2243         },
2244
2245
2246         getRelatedTarget: function(ev) {
2247             ev = ev.browserEvent || ev;
2248             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2249             var t = ev.relatedTarget;
2250             if (!t) {
2251                 if (ev.type == "mouseout") {
2252                     t = ev.toElement;
2253                 } else if (ev.type == "mouseover") {
2254                     t = ev.fromElement;
2255                 }
2256             }
2257
2258             return this.resolveTextNode(t);
2259         },
2260
2261
2262         getTime: function(ev) {
2263             ev = ev.browserEvent || ev;
2264             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2265             if (!ev.time) {
2266                 var t = new Date().getTime();
2267                 try {
2268                     ev.time = t;
2269                 } catch(ex) {
2270                     this.lastError = ex;
2271                     return t;
2272                 }
2273             }
2274
2275             return ev.time;
2276         },
2277
2278
2279         stopEvent: function(ev) {
2280             this.stopPropagation(ev);
2281             this.preventDefault(ev);
2282         },
2283
2284
2285         stopPropagation: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             if (ev.stopPropagation) {
2288                 ev.stopPropagation();
2289             } else {
2290                 ev.cancelBubble = true;
2291             }
2292         },
2293
2294
2295         preventDefault: function(ev) {
2296             ev = ev.browserEvent || ev;
2297             if(ev.preventDefault) {
2298                 ev.preventDefault();
2299             } else {
2300                 ev.returnValue = false;
2301             }
2302         },
2303
2304
2305         getEvent: function(e) {
2306             var ev = e || window.event;
2307             if (!ev) {
2308                 var c = this.getEvent.caller;
2309                 while (c) {
2310                     ev = c.arguments[0];
2311                     if (ev && Event == ev.constructor) {
2312                         break;
2313                     }
2314                     c = c.caller;
2315                 }
2316             }
2317             return ev;
2318         },
2319
2320
2321         getCharCode: function(ev) {
2322             ev = ev.browserEvent || ev;
2323             return ev.charCode || ev.keyCode || 0;
2324         },
2325
2326
2327         _getCacheIndex: function(el, eventName, fn) {
2328             for (var i = 0,len = listeners.length; i < len; ++i) {
2329                 var li = listeners[i];
2330                 if (li &&
2331                     li[this.FN] == fn &&
2332                     li[this.EL] == el &&
2333                     li[this.TYPE] == eventName) {
2334                     return i;
2335                 }
2336             }
2337
2338             return -1;
2339         },
2340
2341
2342         elCache: {},
2343
2344
2345         getEl: function(id) {
2346             return document.getElementById(id);
2347         },
2348
2349
2350         clearCache: function() {
2351         },
2352
2353
2354         _load: function(e) {
2355             loadComplete = true;
2356             var EU = Roo.lib.Event;
2357
2358
2359             if (Roo.isIE) {
2360                 EU.doRemove(window, "load", EU._load);
2361             }
2362         },
2363
2364
2365         _tryPreloadAttach: function() {
2366
2367             if (this.locked) {
2368                 return false;
2369             }
2370
2371             this.locked = true;
2372
2373
2374             var tryAgain = !loadComplete;
2375             if (!tryAgain) {
2376                 tryAgain = (retryCount > 0);
2377             }
2378
2379
2380             var notAvail = [];
2381             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2382                 var item = onAvailStack[i];
2383                 if (item) {
2384                     var el = this.getEl(item.id);
2385
2386                     if (el) {
2387                         if (!item.checkReady ||
2388                             loadComplete ||
2389                             el.nextSibling ||
2390                             (document && document.body)) {
2391
2392                             var scope = el;
2393                             if (item.override) {
2394                                 if (item.override === true) {
2395                                     scope = item.obj;
2396                                 } else {
2397                                     scope = item.override;
2398                                 }
2399                             }
2400                             item.fn.call(scope, item.obj);
2401                             onAvailStack[i] = null;
2402                         }
2403                     } else {
2404                         notAvail.push(item);
2405                     }
2406                 }
2407             }
2408
2409             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2410
2411             if (tryAgain) {
2412
2413                 this.startInterval();
2414             } else {
2415                 clearInterval(this._interval);
2416                 this._interval = null;
2417             }
2418
2419             this.locked = false;
2420
2421             return true;
2422
2423         },
2424
2425
2426         purgeElement: function(el, recurse, eventName) {
2427             var elListeners = this.getListeners(el, eventName);
2428             if (elListeners) {
2429                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2430                     var l = elListeners[i];
2431                     this.removeListener(el, l.type, l.fn);
2432                 }
2433             }
2434
2435             if (recurse && el && el.childNodes) {
2436                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2437                     this.purgeElement(el.childNodes[i], recurse, eventName);
2438                 }
2439             }
2440         },
2441
2442
2443         getListeners: function(el, eventName) {
2444             var results = [], searchLists;
2445             if (!eventName) {
2446                 searchLists = [listeners, unloadListeners];
2447             } else if (eventName == "unload") {
2448                 searchLists = [unloadListeners];
2449             } else {
2450                 searchLists = [listeners];
2451             }
2452
2453             for (var j = 0; j < searchLists.length; ++j) {
2454                 var searchList = searchLists[j];
2455                 if (searchList && searchList.length > 0) {
2456                     for (var i = 0,len = searchList.length; i < len; ++i) {
2457                         var l = searchList[i];
2458                         if (l && l[this.EL] === el &&
2459                             (!eventName || eventName === l[this.TYPE])) {
2460                             results.push({
2461                                 type:   l[this.TYPE],
2462                                 fn:     l[this.FN],
2463                                 obj:    l[this.OBJ],
2464                                 adjust: l[this.ADJ_SCOPE],
2465                                 index:  i
2466                             });
2467                         }
2468                     }
2469                 }
2470             }
2471
2472             return (results.length) ? results : null;
2473         },
2474
2475
2476         _unload: function(e) {
2477
2478             var EU = Roo.lib.Event, i, j, l, len, index;
2479
2480             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2481                 l = unloadListeners[i];
2482                 if (l) {
2483                     var scope = window;
2484                     if (l[EU.ADJ_SCOPE]) {
2485                         if (l[EU.ADJ_SCOPE] === true) {
2486                             scope = l[EU.OBJ];
2487                         } else {
2488                             scope = l[EU.ADJ_SCOPE];
2489                         }
2490                     }
2491                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2492                     unloadListeners[i] = null;
2493                     l = null;
2494                     scope = null;
2495                 }
2496             }
2497
2498             unloadListeners = null;
2499
2500             if (listeners && listeners.length > 0) {
2501                 j = listeners.length;
2502                 while (j) {
2503                     index = j - 1;
2504                     l = listeners[index];
2505                     if (l) {
2506                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2507                                 l[EU.FN], index);
2508                     }
2509                     j = j - 1;
2510                 }
2511                 l = null;
2512
2513                 EU.clearCache();
2514             }
2515
2516             EU.doRemove(window, "unload", EU._unload);
2517
2518         },
2519
2520
2521         getScroll: function() {
2522             var dd = document.documentElement, db = document.body;
2523             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2524                 return [dd.scrollTop, dd.scrollLeft];
2525             } else if (db) {
2526                 return [db.scrollTop, db.scrollLeft];
2527             } else {
2528                 return [0, 0];
2529             }
2530         },
2531
2532
2533         doAdd: function () {
2534             if (window.addEventListener) {
2535                 return function(el, eventName, fn, capture) {
2536                     el.addEventListener(eventName, fn, (capture));
2537                 };
2538             } else if (window.attachEvent) {
2539                 return function(el, eventName, fn, capture) {
2540                     el.attachEvent("on" + eventName, fn);
2541                 };
2542             } else {
2543                 return function() {
2544                 };
2545             }
2546         }(),
2547
2548
2549         doRemove: function() {
2550             if (window.removeEventListener) {
2551                 return function (el, eventName, fn, capture) {
2552                     el.removeEventListener(eventName, fn, (capture));
2553                 };
2554             } else if (window.detachEvent) {
2555                 return function (el, eventName, fn) {
2556                     el.detachEvent("on" + eventName, fn);
2557                 };
2558             } else {
2559                 return function() {
2560                 };
2561             }
2562         }()
2563     };
2564     
2565 }();
2566 (function() {     
2567    
2568     var E = Roo.lib.Event;
2569     E.on = E.addListener;
2570     E.un = E.removeListener;
2571
2572     if (document && document.body) {
2573         E._load();
2574     } else {
2575         E.doAdd(window, "load", E._load);
2576     }
2577     E.doAdd(window, "unload", E._unload);
2578     E._tryPreloadAttach();
2579 })();
2580
2581 /*
2582  * Portions of this file are based on pieces of Yahoo User Interface Library
2583  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2584  * YUI licensed under the BSD License:
2585  * http://developer.yahoo.net/yui/license.txt
2586  * <script type="text/javascript">
2587  *
2588  */
2589
2590 (function() {
2591     /**
2592      * @class Roo.lib.Ajax
2593      *
2594      */
2595     Roo.lib.Ajax = {
2596         /**
2597          * @static 
2598          */
2599         request : function(method, uri, cb, data, options) {
2600             if(options){
2601                 var hs = options.headers;
2602                 if(hs){
2603                     for(var h in hs){
2604                         if(hs.hasOwnProperty(h)){
2605                             this.initHeader(h, hs[h], false);
2606                         }
2607                     }
2608                 }
2609                 if(options.xmlData){
2610                     this.initHeader('Content-Type', 'text/xml', false);
2611                     method = 'POST';
2612                     data = options.xmlData;
2613                 }
2614             }
2615
2616             return this.asyncRequest(method, uri, cb, data);
2617         },
2618
2619         serializeForm : function(form) {
2620             if(typeof form == 'string') {
2621                 form = (document.getElementById(form) || document.forms[form]);
2622             }
2623
2624             var el, name, val, disabled, data = '', hasSubmit = false;
2625             for (var i = 0; i < form.elements.length; i++) {
2626                 el = form.elements[i];
2627                 disabled = form.elements[i].disabled;
2628                 name = form.elements[i].name;
2629                 val = form.elements[i].value;
2630
2631                 if (!disabled && name){
2632                     switch (el.type)
2633                             {
2634                         case 'select-one':
2635                         case 'select-multiple':
2636                             for (var j = 0; j < el.options.length; j++) {
2637                                 if (el.options[j].selected) {
2638                                     if (Roo.isIE) {
2639                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2640                                     }
2641                                     else {
2642                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2643                                     }
2644                                 }
2645                             }
2646                             break;
2647                         case 'radio':
2648                         case 'checkbox':
2649                             if (el.checked) {
2650                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2651                             }
2652                             break;
2653                         case 'file':
2654
2655                         case undefined:
2656
2657                         case 'reset':
2658
2659                         case 'button':
2660
2661                             break;
2662                         case 'submit':
2663                             if(hasSubmit == false) {
2664                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2665                                 hasSubmit = true;
2666                             }
2667                             break;
2668                         default:
2669                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2670                             break;
2671                     }
2672                 }
2673             }
2674             data = data.substr(0, data.length - 1);
2675             return data;
2676         },
2677
2678         headers:{},
2679
2680         hasHeaders:false,
2681
2682         useDefaultHeader:true,
2683
2684         defaultPostHeader:'application/x-www-form-urlencoded',
2685
2686         useDefaultXhrHeader:true,
2687
2688         defaultXhrHeader:'XMLHttpRequest',
2689
2690         hasDefaultHeaders:true,
2691
2692         defaultHeaders:{},
2693
2694         poll:{},
2695
2696         timeout:{},
2697
2698         pollInterval:50,
2699
2700         transactionId:0,
2701
2702         setProgId:function(id)
2703         {
2704             this.activeX.unshift(id);
2705         },
2706
2707         setDefaultPostHeader:function(b)
2708         {
2709             this.useDefaultHeader = b;
2710         },
2711
2712         setDefaultXhrHeader:function(b)
2713         {
2714             this.useDefaultXhrHeader = b;
2715         },
2716
2717         setPollingInterval:function(i)
2718         {
2719             if (typeof i == 'number' && isFinite(i)) {
2720                 this.pollInterval = i;
2721             }
2722         },
2723
2724         createXhrObject:function(transactionId)
2725         {
2726             var obj,http;
2727             try
2728             {
2729
2730                 http = new XMLHttpRequest();
2731
2732                 obj = { conn:http, tId:transactionId };
2733             }
2734             catch(e)
2735             {
2736                 for (var i = 0; i < this.activeX.length; ++i) {
2737                     try
2738                     {
2739
2740                         http = new ActiveXObject(this.activeX[i]);
2741
2742                         obj = { conn:http, tId:transactionId };
2743                         break;
2744                     }
2745                     catch(e) {
2746                     }
2747                 }
2748             }
2749             finally
2750             {
2751                 return obj;
2752             }
2753         },
2754
2755         getConnectionObject:function()
2756         {
2757             var o;
2758             var tId = this.transactionId;
2759
2760             try
2761             {
2762                 o = this.createXhrObject(tId);
2763                 if (o) {
2764                     this.transactionId++;
2765                 }
2766             }
2767             catch(e) {
2768             }
2769             finally
2770             {
2771                 return o;
2772             }
2773         },
2774
2775         asyncRequest:function(method, uri, callback, postData)
2776         {
2777             var o = this.getConnectionObject();
2778
2779             if (!o) {
2780                 return null;
2781             }
2782             else {
2783                 o.conn.open(method, uri, true);
2784
2785                 if (this.useDefaultXhrHeader) {
2786                     if (!this.defaultHeaders['X-Requested-With']) {
2787                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2788                     }
2789                 }
2790
2791                 if(postData && this.useDefaultHeader){
2792                     this.initHeader('Content-Type', this.defaultPostHeader);
2793                 }
2794
2795                  if (this.hasDefaultHeaders || this.hasHeaders) {
2796                     this.setHeader(o);
2797                 }
2798
2799                 this.handleReadyState(o, callback);
2800                 o.conn.send(postData || null);
2801
2802                 return o;
2803             }
2804         },
2805
2806         handleReadyState:function(o, callback)
2807         {
2808             var oConn = this;
2809
2810             if (callback && callback.timeout) {
2811                 
2812                 this.timeout[o.tId] = window.setTimeout(function() {
2813                     oConn.abort(o, callback, true);
2814                 }, callback.timeout);
2815             }
2816
2817             this.poll[o.tId] = window.setInterval(
2818                     function() {
2819                         if (o.conn && o.conn.readyState == 4) {
2820                             window.clearInterval(oConn.poll[o.tId]);
2821                             delete oConn.poll[o.tId];
2822
2823                             if(callback && callback.timeout) {
2824                                 window.clearTimeout(oConn.timeout[o.tId]);
2825                                 delete oConn.timeout[o.tId];
2826                             }
2827
2828                             oConn.handleTransactionResponse(o, callback);
2829                         }
2830                     }
2831                     , this.pollInterval);
2832         },
2833
2834         handleTransactionResponse:function(o, callback, isAbort)
2835         {
2836
2837             if (!callback) {
2838                 this.releaseObject(o);
2839                 return;
2840             }
2841
2842             var httpStatus, responseObject;
2843
2844             try
2845             {
2846                 if (o.conn.status !== undefined && o.conn.status != 0) {
2847                     httpStatus = o.conn.status;
2848                 }
2849                 else {
2850                     httpStatus = 13030;
2851                 }
2852             }
2853             catch(e) {
2854
2855
2856                 httpStatus = 13030;
2857             }
2858
2859             if (httpStatus >= 200 && httpStatus < 300) {
2860                 responseObject = this.createResponseObject(o, callback.argument);
2861                 if (callback.success) {
2862                     if (!callback.scope) {
2863                         callback.success(responseObject);
2864                     }
2865                     else {
2866
2867
2868                         callback.success.apply(callback.scope, [responseObject]);
2869                     }
2870                 }
2871             }
2872             else {
2873                 switch (httpStatus) {
2874
2875                     case 12002:
2876                     case 12029:
2877                     case 12030:
2878                     case 12031:
2879                     case 12152:
2880                     case 13030:
2881                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2882                         if (callback.failure) {
2883                             if (!callback.scope) {
2884                                 callback.failure(responseObject);
2885                             }
2886                             else {
2887                                 callback.failure.apply(callback.scope, [responseObject]);
2888                             }
2889                         }
2890                         break;
2891                     default:
2892                         responseObject = this.createResponseObject(o, callback.argument);
2893                         if (callback.failure) {
2894                             if (!callback.scope) {
2895                                 callback.failure(responseObject);
2896                             }
2897                             else {
2898                                 callback.failure.apply(callback.scope, [responseObject]);
2899                             }
2900                         }
2901                 }
2902             }
2903
2904             this.releaseObject(o);
2905             responseObject = null;
2906         },
2907
2908         createResponseObject:function(o, callbackArg)
2909         {
2910             var obj = {};
2911             var headerObj = {};
2912
2913             try
2914             {
2915                 var headerStr = o.conn.getAllResponseHeaders();
2916                 var header = headerStr.split('\n');
2917                 for (var i = 0; i < header.length; i++) {
2918                     var delimitPos = header[i].indexOf(':');
2919                     if (delimitPos != -1) {
2920                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2921                     }
2922                 }
2923             }
2924             catch(e) {
2925             }
2926
2927             obj.tId = o.tId;
2928             obj.status = o.conn.status;
2929             obj.statusText = o.conn.statusText;
2930             obj.getResponseHeader = headerObj;
2931             obj.getAllResponseHeaders = headerStr;
2932             obj.responseText = o.conn.responseText;
2933             obj.responseXML = o.conn.responseXML;
2934
2935             if (typeof callbackArg !== undefined) {
2936                 obj.argument = callbackArg;
2937             }
2938
2939             return obj;
2940         },
2941
2942         createExceptionObject:function(tId, callbackArg, isAbort)
2943         {
2944             var COMM_CODE = 0;
2945             var COMM_ERROR = 'communication failure';
2946             var ABORT_CODE = -1;
2947             var ABORT_ERROR = 'transaction aborted';
2948
2949             var obj = {};
2950
2951             obj.tId = tId;
2952             if (isAbort) {
2953                 obj.status = ABORT_CODE;
2954                 obj.statusText = ABORT_ERROR;
2955             }
2956             else {
2957                 obj.status = COMM_CODE;
2958                 obj.statusText = COMM_ERROR;
2959             }
2960
2961             if (callbackArg) {
2962                 obj.argument = callbackArg;
2963             }
2964
2965             return obj;
2966         },
2967
2968         initHeader:function(label, value, isDefault)
2969         {
2970             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2971
2972             if (headerObj[label] === undefined) {
2973                 headerObj[label] = value;
2974             }
2975             else {
2976
2977
2978                 headerObj[label] = value + "," + headerObj[label];
2979             }
2980
2981             if (isDefault) {
2982                 this.hasDefaultHeaders = true;
2983             }
2984             else {
2985                 this.hasHeaders = true;
2986             }
2987         },
2988
2989
2990         setHeader:function(o)
2991         {
2992             if (this.hasDefaultHeaders) {
2993                 for (var prop in this.defaultHeaders) {
2994                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2995                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2996                     }
2997                 }
2998             }
2999
3000             if (this.hasHeaders) {
3001                 for (var prop in this.headers) {
3002                     if (this.headers.hasOwnProperty(prop)) {
3003                         o.conn.setRequestHeader(prop, this.headers[prop]);
3004                     }
3005                 }
3006                 this.headers = {};
3007                 this.hasHeaders = false;
3008             }
3009         },
3010
3011         resetDefaultHeaders:function() {
3012             delete this.defaultHeaders;
3013             this.defaultHeaders = {};
3014             this.hasDefaultHeaders = false;
3015         },
3016
3017         abort:function(o, callback, isTimeout)
3018         {
3019             if(this.isCallInProgress(o)) {
3020                 o.conn.abort();
3021                 window.clearInterval(this.poll[o.tId]);
3022                 delete this.poll[o.tId];
3023                 if (isTimeout) {
3024                     delete this.timeout[o.tId];
3025                 }
3026
3027                 this.handleTransactionResponse(o, callback, true);
3028
3029                 return true;
3030             }
3031             else {
3032                 return false;
3033             }
3034         },
3035
3036
3037         isCallInProgress:function(o)
3038         {
3039             if (o && o.conn) {
3040                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3041             }
3042             else {
3043
3044                 return false;
3045             }
3046         },
3047
3048
3049         releaseObject:function(o)
3050         {
3051
3052             o.conn = null;
3053
3054             o = null;
3055         },
3056
3057         activeX:[
3058         'MSXML2.XMLHTTP.3.0',
3059         'MSXML2.XMLHTTP',
3060         'Microsoft.XMLHTTP'
3061         ]
3062
3063
3064     };
3065 })();/*
3066  * Portions of this file are based on pieces of Yahoo User Interface Library
3067  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3068  * YUI licensed under the BSD License:
3069  * http://developer.yahoo.net/yui/license.txt
3070  * <script type="text/javascript">
3071  *
3072  */
3073
3074 Roo.lib.Region = function(t, r, b, l) {
3075     this.top = t;
3076     this[1] = t;
3077     this.right = r;
3078     this.bottom = b;
3079     this.left = l;
3080     this[0] = l;
3081 };
3082
3083
3084 Roo.lib.Region.prototype = {
3085     contains : function(region) {
3086         return ( region.left >= this.left &&
3087                  region.right <= this.right &&
3088                  region.top >= this.top &&
3089                  region.bottom <= this.bottom    );
3090
3091     },
3092
3093     getArea : function() {
3094         return ( (this.bottom - this.top) * (this.right - this.left) );
3095     },
3096
3097     intersect : function(region) {
3098         var t = Math.max(this.top, region.top);
3099         var r = Math.min(this.right, region.right);
3100         var b = Math.min(this.bottom, region.bottom);
3101         var l = Math.max(this.left, region.left);
3102
3103         if (b >= t && r >= l) {
3104             return new Roo.lib.Region(t, r, b, l);
3105         } else {
3106             return null;
3107         }
3108     },
3109     union : function(region) {
3110         var t = Math.min(this.top, region.top);
3111         var r = Math.max(this.right, region.right);
3112         var b = Math.max(this.bottom, region.bottom);
3113         var l = Math.min(this.left, region.left);
3114
3115         return new Roo.lib.Region(t, r, b, l);
3116     },
3117
3118     adjust : function(t, l, b, r) {
3119         this.top += t;
3120         this.left += l;
3121         this.right += r;
3122         this.bottom += b;
3123         return this;
3124     }
3125 };
3126
3127 Roo.lib.Region.getRegion = function(el) {
3128     var p = Roo.lib.Dom.getXY(el);
3129
3130     var t = p[1];
3131     var r = p[0] + el.offsetWidth;
3132     var b = p[1] + el.offsetHeight;
3133     var l = p[0];
3134
3135     return new Roo.lib.Region(t, r, b, l);
3136 };
3137 /*
3138  * Portions of this file are based on pieces of Yahoo User Interface Library
3139  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3140  * YUI licensed under the BSD License:
3141  * http://developer.yahoo.net/yui/license.txt
3142  * <script type="text/javascript">
3143  *
3144  */
3145 //@@dep Roo.lib.Region
3146
3147
3148 Roo.lib.Point = function(x, y) {
3149     if (x instanceof Array) {
3150         y = x[1];
3151         x = x[0];
3152     }
3153     this.x = this.right = this.left = this[0] = x;
3154     this.y = this.top = this.bottom = this[1] = y;
3155 };
3156
3157 Roo.lib.Point.prototype = new Roo.lib.Region();
3158 /*
3159  * Portions of this file are based on pieces of Yahoo User Interface Library
3160  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3161  * YUI licensed under the BSD License:
3162  * http://developer.yahoo.net/yui/license.txt
3163  * <script type="text/javascript">
3164  *
3165  */
3166  
3167 (function() {   
3168
3169     Roo.lib.Anim = {
3170         scroll : function(el, args, duration, easing, cb, scope) {
3171             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3172         },
3173
3174         motion : function(el, args, duration, easing, cb, scope) {
3175             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3176         },
3177
3178         color : function(el, args, duration, easing, cb, scope) {
3179             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3180         },
3181
3182         run : function(el, args, duration, easing, cb, scope, type) {
3183             type = type || Roo.lib.AnimBase;
3184             if (typeof easing == "string") {
3185                 easing = Roo.lib.Easing[easing];
3186             }
3187             var anim = new type(el, args, duration, easing);
3188             anim.animateX(function() {
3189                 Roo.callback(cb, scope);
3190             });
3191             return anim;
3192         }
3193     };
3194 })();/*
3195  * Portions of this file are based on pieces of Yahoo User Interface Library
3196  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3197  * YUI licensed under the BSD License:
3198  * http://developer.yahoo.net/yui/license.txt
3199  * <script type="text/javascript">
3200  *
3201  */
3202
3203 (function() {    
3204     var libFlyweight;
3205     
3206     function fly(el) {
3207         if (!libFlyweight) {
3208             libFlyweight = new Roo.Element.Flyweight();
3209         }
3210         libFlyweight.dom = el;
3211         return libFlyweight;
3212     }
3213
3214     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3215     
3216    
3217     
3218     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3219         if (el) {
3220             this.init(el, attributes, duration, method);
3221         }
3222     };
3223
3224     Roo.lib.AnimBase.fly = fly;
3225     
3226     
3227     
3228     Roo.lib.AnimBase.prototype = {
3229
3230         toString: function() {
3231             var el = this.getEl();
3232             var id = el.id || el.tagName;
3233             return ("Anim " + id);
3234         },
3235
3236         patterns: {
3237             noNegatives:        /width|height|opacity|padding/i,
3238             offsetAttribute:  /^((width|height)|(top|left))$/,
3239             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3240             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3241         },
3242
3243
3244         doMethod: function(attr, start, end) {
3245             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3246         },
3247
3248
3249         setAttribute: function(attr, val, unit) {
3250             if (this.patterns.noNegatives.test(attr)) {
3251                 val = (val > 0) ? val : 0;
3252             }
3253
3254             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3255         },
3256
3257
3258         getAttribute: function(attr) {
3259             var el = this.getEl();
3260             var val = fly(el).getStyle(attr);
3261
3262             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3263                 return parseFloat(val);
3264             }
3265
3266             var a = this.patterns.offsetAttribute.exec(attr) || [];
3267             var pos = !!( a[3] );
3268             var box = !!( a[2] );
3269
3270
3271             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3272                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3273             } else {
3274                 val = 0;
3275             }
3276
3277             return val;
3278         },
3279
3280
3281         getDefaultUnit: function(attr) {
3282             if (this.patterns.defaultUnit.test(attr)) {
3283                 return 'px';
3284             }
3285
3286             return '';
3287         },
3288
3289         animateX : function(callback, scope) {
3290             var f = function() {
3291                 this.onComplete.removeListener(f);
3292                 if (typeof callback == "function") {
3293                     callback.call(scope || this, this);
3294                 }
3295             };
3296             this.onComplete.addListener(f, this);
3297             this.animate();
3298         },
3299
3300
3301         setRuntimeAttribute: function(attr) {
3302             var start;
3303             var end;
3304             var attributes = this.attributes;
3305
3306             this.runtimeAttributes[attr] = {};
3307
3308             var isset = function(prop) {
3309                 return (typeof prop !== 'undefined');
3310             };
3311
3312             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3313                 return false;
3314             }
3315
3316             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3317
3318
3319             if (isset(attributes[attr]['to'])) {
3320                 end = attributes[attr]['to'];
3321             } else if (isset(attributes[attr]['by'])) {
3322                 if (start.constructor == Array) {
3323                     end = [];
3324                     for (var i = 0, len = start.length; i < len; ++i) {
3325                         end[i] = start[i] + attributes[attr]['by'][i];
3326                     }
3327                 } else {
3328                     end = start + attributes[attr]['by'];
3329                 }
3330             }
3331
3332             this.runtimeAttributes[attr].start = start;
3333             this.runtimeAttributes[attr].end = end;
3334
3335
3336             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3337         },
3338
3339
3340         init: function(el, attributes, duration, method) {
3341
3342             var isAnimated = false;
3343
3344
3345             var startTime = null;
3346
3347
3348             var actualFrames = 0;
3349
3350
3351             el = Roo.getDom(el);
3352
3353
3354             this.attributes = attributes || {};
3355
3356
3357             this.duration = duration || 1;
3358
3359
3360             this.method = method || Roo.lib.Easing.easeNone;
3361
3362
3363             this.useSeconds = true;
3364
3365
3366             this.currentFrame = 0;
3367
3368
3369             this.totalFrames = Roo.lib.AnimMgr.fps;
3370
3371
3372             this.getEl = function() {
3373                 return el;
3374             };
3375
3376
3377             this.isAnimated = function() {
3378                 return isAnimated;
3379             };
3380
3381
3382             this.getStartTime = function() {
3383                 return startTime;
3384             };
3385
3386             this.runtimeAttributes = {};
3387
3388
3389             this.animate = function() {
3390                 if (this.isAnimated()) {
3391                     return false;
3392                 }
3393
3394                 this.currentFrame = 0;
3395
3396                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3397
3398                 Roo.lib.AnimMgr.registerElement(this);
3399             };
3400
3401
3402             this.stop = function(finish) {
3403                 if (finish) {
3404                     this.currentFrame = this.totalFrames;
3405                     this._onTween.fire();
3406                 }
3407                 Roo.lib.AnimMgr.stop(this);
3408             };
3409
3410             var onStart = function() {
3411                 this.onStart.fire();
3412
3413                 this.runtimeAttributes = {};
3414                 for (var attr in this.attributes) {
3415                     this.setRuntimeAttribute(attr);
3416                 }
3417
3418                 isAnimated = true;
3419                 actualFrames = 0;
3420                 startTime = new Date();
3421             };
3422
3423
3424             var onTween = function() {
3425                 var data = {
3426                     duration: new Date() - this.getStartTime(),
3427                     currentFrame: this.currentFrame
3428                 };
3429
3430                 data.toString = function() {
3431                     return (
3432                             'duration: ' + data.duration +
3433                             ', currentFrame: ' + data.currentFrame
3434                             );
3435                 };
3436
3437                 this.onTween.fire(data);
3438
3439                 var runtimeAttributes = this.runtimeAttributes;
3440
3441                 for (var attr in runtimeAttributes) {
3442                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3443                 }
3444
3445                 actualFrames += 1;
3446             };
3447
3448             var onComplete = function() {
3449                 var actual_duration = (new Date() - startTime) / 1000 ;
3450
3451                 var data = {
3452                     duration: actual_duration,
3453                     frames: actualFrames,
3454                     fps: actualFrames / actual_duration
3455                 };
3456
3457                 data.toString = function() {
3458                     return (
3459                             'duration: ' + data.duration +
3460                             ', frames: ' + data.frames +
3461                             ', fps: ' + data.fps
3462                             );
3463                 };
3464
3465                 isAnimated = false;
3466                 actualFrames = 0;
3467                 this.onComplete.fire(data);
3468             };
3469
3470
3471             this._onStart = new Roo.util.Event(this);
3472             this.onStart = new Roo.util.Event(this);
3473             this.onTween = new Roo.util.Event(this);
3474             this._onTween = new Roo.util.Event(this);
3475             this.onComplete = new Roo.util.Event(this);
3476             this._onComplete = new Roo.util.Event(this);
3477             this._onStart.addListener(onStart);
3478             this._onTween.addListener(onTween);
3479             this._onComplete.addListener(onComplete);
3480         }
3481     };
3482 })();
3483 /*
3484  * Portions of this file are based on pieces of Yahoo User Interface Library
3485  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3486  * YUI licensed under the BSD License:
3487  * http://developer.yahoo.net/yui/license.txt
3488  * <script type="text/javascript">
3489  *
3490  */
3491
3492 Roo.lib.AnimMgr = new function() {
3493
3494     var thread = null;
3495
3496
3497     var queue = [];
3498
3499
3500     var tweenCount = 0;
3501
3502
3503     this.fps = 1000;
3504
3505
3506     this.delay = 1;
3507
3508
3509     this.registerElement = function(tween) {
3510         queue[queue.length] = tween;
3511         tweenCount += 1;
3512         tween._onStart.fire();
3513         this.start();
3514     };
3515
3516
3517     this.unRegister = function(tween, index) {
3518         tween._onComplete.fire();
3519         index = index || getIndex(tween);
3520         if (index != -1) {
3521             queue.splice(index, 1);
3522         }
3523
3524         tweenCount -= 1;
3525         if (tweenCount <= 0) {
3526             this.stop();
3527         }
3528     };
3529
3530
3531     this.start = function() {
3532         if (thread === null) {
3533             thread = setInterval(this.run, this.delay);
3534         }
3535     };
3536
3537
3538     this.stop = function(tween) {
3539         if (!tween) {
3540             clearInterval(thread);
3541
3542             for (var i = 0, len = queue.length; i < len; ++i) {
3543                 if (queue[0].isAnimated()) {
3544                     this.unRegister(queue[0], 0);
3545                 }
3546             }
3547
3548             queue = [];
3549             thread = null;
3550             tweenCount = 0;
3551         }
3552         else {
3553             this.unRegister(tween);
3554         }
3555     };
3556
3557
3558     this.run = function() {
3559         for (var i = 0, len = queue.length; i < len; ++i) {
3560             var tween = queue[i];
3561             if (!tween || !tween.isAnimated()) {
3562                 continue;
3563             }
3564
3565             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3566             {
3567                 tween.currentFrame += 1;
3568
3569                 if (tween.useSeconds) {
3570                     correctFrame(tween);
3571                 }
3572                 tween._onTween.fire();
3573             }
3574             else {
3575                 Roo.lib.AnimMgr.stop(tween, i);
3576             }
3577         }
3578     };
3579
3580     var getIndex = function(anim) {
3581         for (var i = 0, len = queue.length; i < len; ++i) {
3582             if (queue[i] == anim) {
3583                 return i;
3584             }
3585         }
3586         return -1;
3587     };
3588
3589
3590     var correctFrame = function(tween) {
3591         var frames = tween.totalFrames;
3592         var frame = tween.currentFrame;
3593         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3594         var elapsed = (new Date() - tween.getStartTime());
3595         var tweak = 0;
3596
3597         if (elapsed < tween.duration * 1000) {
3598             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3599         } else {
3600             tweak = frames - (frame + 1);
3601         }
3602         if (tweak > 0 && isFinite(tweak)) {
3603             if (tween.currentFrame + tweak >= frames) {
3604                 tweak = frames - (frame + 1);
3605             }
3606
3607             tween.currentFrame += tweak;
3608         }
3609     };
3610 };
3611
3612     /*
3613  * Portions of this file are based on pieces of Yahoo User Interface Library
3614  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3615  * YUI licensed under the BSD License:
3616  * http://developer.yahoo.net/yui/license.txt
3617  * <script type="text/javascript">
3618  *
3619  */
3620 Roo.lib.Bezier = new function() {
3621
3622         this.getPosition = function(points, t) {
3623             var n = points.length;
3624             var tmp = [];
3625
3626             for (var i = 0; i < n; ++i) {
3627                 tmp[i] = [points[i][0], points[i][1]];
3628             }
3629
3630             for (var j = 1; j < n; ++j) {
3631                 for (i = 0; i < n - j; ++i) {
3632                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3633                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3634                 }
3635             }
3636
3637             return [ tmp[0][0], tmp[0][1] ];
3638
3639         };
3640     };/*
3641  * Portions of this file are based on pieces of Yahoo User Interface Library
3642  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3643  * YUI licensed under the BSD License:
3644  * http://developer.yahoo.net/yui/license.txt
3645  * <script type="text/javascript">
3646  *
3647  */
3648 (function() {
3649
3650     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3651         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3652     };
3653
3654     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3655
3656     var fly = Roo.lib.AnimBase.fly;
3657     var Y = Roo.lib;
3658     var superclass = Y.ColorAnim.superclass;
3659     var proto = Y.ColorAnim.prototype;
3660
3661     proto.toString = function() {
3662         var el = this.getEl();
3663         var id = el.id || el.tagName;
3664         return ("ColorAnim " + id);
3665     };
3666
3667     proto.patterns.color = /color$/i;
3668     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3669     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3670     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3671     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3672
3673
3674     proto.parseColor = function(s) {
3675         if (s.length == 3) {
3676             return s;
3677         }
3678
3679         var c = this.patterns.hex.exec(s);
3680         if (c && c.length == 4) {
3681             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3682         }
3683
3684         c = this.patterns.rgb.exec(s);
3685         if (c && c.length == 4) {
3686             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3687         }
3688
3689         c = this.patterns.hex3.exec(s);
3690         if (c && c.length == 4) {
3691             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3692         }
3693
3694         return null;
3695     };
3696     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3697     proto.getAttribute = function(attr) {
3698         var el = this.getEl();
3699         if (this.patterns.color.test(attr)) {
3700             var val = fly(el).getStyle(attr);
3701
3702             if (this.patterns.transparent.test(val)) {
3703                 var parent = el.parentNode;
3704                 val = fly(parent).getStyle(attr);
3705
3706                 while (parent && this.patterns.transparent.test(val)) {
3707                     parent = parent.parentNode;
3708                     val = fly(parent).getStyle(attr);
3709                     if (parent.tagName.toUpperCase() == 'HTML') {
3710                         val = '#fff';
3711                     }
3712                 }
3713             }
3714         } else {
3715             val = superclass.getAttribute.call(this, attr);
3716         }
3717
3718         return val;
3719     };
3720     proto.getAttribute = function(attr) {
3721         var el = this.getEl();
3722         if (this.patterns.color.test(attr)) {
3723             var val = fly(el).getStyle(attr);
3724
3725             if (this.patterns.transparent.test(val)) {
3726                 var parent = el.parentNode;
3727                 val = fly(parent).getStyle(attr);
3728
3729                 while (parent && this.patterns.transparent.test(val)) {
3730                     parent = parent.parentNode;
3731                     val = fly(parent).getStyle(attr);
3732                     if (parent.tagName.toUpperCase() == 'HTML') {
3733                         val = '#fff';
3734                     }
3735                 }
3736             }
3737         } else {
3738             val = superclass.getAttribute.call(this, attr);
3739         }
3740
3741         return val;
3742     };
3743
3744     proto.doMethod = function(attr, start, end) {
3745         var val;
3746
3747         if (this.patterns.color.test(attr)) {
3748             val = [];
3749             for (var i = 0, len = start.length; i < len; ++i) {
3750                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3751             }
3752
3753             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3754         }
3755         else {
3756             val = superclass.doMethod.call(this, attr, start, end);
3757         }
3758
3759         return val;
3760     };
3761
3762     proto.setRuntimeAttribute = function(attr) {
3763         superclass.setRuntimeAttribute.call(this, attr);
3764
3765         if (this.patterns.color.test(attr)) {
3766             var attributes = this.attributes;
3767             var start = this.parseColor(this.runtimeAttributes[attr].start);
3768             var end = this.parseColor(this.runtimeAttributes[attr].end);
3769
3770             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3771                 end = this.parseColor(attributes[attr].by);
3772
3773                 for (var i = 0, len = start.length; i < len; ++i) {
3774                     end[i] = start[i] + end[i];
3775                 }
3776             }
3777
3778             this.runtimeAttributes[attr].start = start;
3779             this.runtimeAttributes[attr].end = end;
3780         }
3781     };
3782 })();
3783
3784 /*
3785  * Portions of this file are based on pieces of Yahoo User Interface Library
3786  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3787  * YUI licensed under the BSD License:
3788  * http://developer.yahoo.net/yui/license.txt
3789  * <script type="text/javascript">
3790  *
3791  */
3792 Roo.lib.Easing = {
3793
3794
3795     easeNone: function (t, b, c, d) {
3796         return c * t / d + b;
3797     },
3798
3799
3800     easeIn: function (t, b, c, d) {
3801         return c * (t /= d) * t + b;
3802     },
3803
3804
3805     easeOut: function (t, b, c, d) {
3806         return -c * (t /= d) * (t - 2) + b;
3807     },
3808
3809
3810     easeBoth: function (t, b, c, d) {
3811         if ((t /= d / 2) < 1) {
3812             return c / 2 * t * t + b;
3813         }
3814
3815         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3816     },
3817
3818
3819     easeInStrong: function (t, b, c, d) {
3820         return c * (t /= d) * t * t * t + b;
3821     },
3822
3823
3824     easeOutStrong: function (t, b, c, d) {
3825         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3826     },
3827
3828
3829     easeBothStrong: function (t, b, c, d) {
3830         if ((t /= d / 2) < 1) {
3831             return c / 2 * t * t * t * t + b;
3832         }
3833
3834         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3835     },
3836
3837
3838
3839     elasticIn: function (t, b, c, d, a, p) {
3840         if (t == 0) {
3841             return b;
3842         }
3843         if ((t /= d) == 1) {
3844             return b + c;
3845         }
3846         if (!p) {
3847             p = d * .3;
3848         }
3849
3850         if (!a || a < Math.abs(c)) {
3851             a = c;
3852             var s = p / 4;
3853         }
3854         else {
3855             var s = p / (2 * Math.PI) * Math.asin(c / a);
3856         }
3857
3858         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3859     },
3860
3861
3862     elasticOut: function (t, b, c, d, a, p) {
3863         if (t == 0) {
3864             return b;
3865         }
3866         if ((t /= d) == 1) {
3867             return b + c;
3868         }
3869         if (!p) {
3870             p = d * .3;
3871         }
3872
3873         if (!a || a < Math.abs(c)) {
3874             a = c;
3875             var s = p / 4;
3876         }
3877         else {
3878             var s = p / (2 * Math.PI) * Math.asin(c / a);
3879         }
3880
3881         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3882     },
3883
3884
3885     elasticBoth: function (t, b, c, d, a, p) {
3886         if (t == 0) {
3887             return b;
3888         }
3889
3890         if ((t /= d / 2) == 2) {
3891             return b + c;
3892         }
3893
3894         if (!p) {
3895             p = d * (.3 * 1.5);
3896         }
3897
3898         if (!a || a < Math.abs(c)) {
3899             a = c;
3900             var s = p / 4;
3901         }
3902         else {
3903             var s = p / (2 * Math.PI) * Math.asin(c / a);
3904         }
3905
3906         if (t < 1) {
3907             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3908                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3909         }
3910         return a * Math.pow(2, -10 * (t -= 1)) *
3911                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3912     },
3913
3914
3915
3916     backIn: function (t, b, c, d, s) {
3917         if (typeof s == 'undefined') {
3918             s = 1.70158;
3919         }
3920         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3921     },
3922
3923
3924     backOut: function (t, b, c, d, s) {
3925         if (typeof s == 'undefined') {
3926             s = 1.70158;
3927         }
3928         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3929     },
3930
3931
3932     backBoth: function (t, b, c, d, s) {
3933         if (typeof s == 'undefined') {
3934             s = 1.70158;
3935         }
3936
3937         if ((t /= d / 2 ) < 1) {
3938             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3939         }
3940         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3941     },
3942
3943
3944     bounceIn: function (t, b, c, d) {
3945         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3946     },
3947
3948
3949     bounceOut: function (t, b, c, d) {
3950         if ((t /= d) < (1 / 2.75)) {
3951             return c * (7.5625 * t * t) + b;
3952         } else if (t < (2 / 2.75)) {
3953             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3954         } else if (t < (2.5 / 2.75)) {
3955             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3956         }
3957         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3958     },
3959
3960
3961     bounceBoth: function (t, b, c, d) {
3962         if (t < d / 2) {
3963             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3964         }
3965         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3966     }
3967 };/*
3968  * Portions of this file are based on pieces of Yahoo User Interface Library
3969  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3970  * YUI licensed under the BSD License:
3971  * http://developer.yahoo.net/yui/license.txt
3972  * <script type="text/javascript">
3973  *
3974  */
3975     (function() {
3976         Roo.lib.Motion = function(el, attributes, duration, method) {
3977             if (el) {
3978                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3979             }
3980         };
3981
3982         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3983
3984
3985         var Y = Roo.lib;
3986         var superclass = Y.Motion.superclass;
3987         var proto = Y.Motion.prototype;
3988
3989         proto.toString = function() {
3990             var el = this.getEl();
3991             var id = el.id || el.tagName;
3992             return ("Motion " + id);
3993         };
3994
3995         proto.patterns.points = /^points$/i;
3996
3997         proto.setAttribute = function(attr, val, unit) {
3998             if (this.patterns.points.test(attr)) {
3999                 unit = unit || 'px';
4000                 superclass.setAttribute.call(this, 'left', val[0], unit);
4001                 superclass.setAttribute.call(this, 'top', val[1], unit);
4002             } else {
4003                 superclass.setAttribute.call(this, attr, val, unit);
4004             }
4005         };
4006
4007         proto.getAttribute = function(attr) {
4008             if (this.patterns.points.test(attr)) {
4009                 var val = [
4010                         superclass.getAttribute.call(this, 'left'),
4011                         superclass.getAttribute.call(this, 'top')
4012                         ];
4013             } else {
4014                 val = superclass.getAttribute.call(this, attr);
4015             }
4016
4017             return val;
4018         };
4019
4020         proto.doMethod = function(attr, start, end) {
4021             var val = null;
4022
4023             if (this.patterns.points.test(attr)) {
4024                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4025                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4026             } else {
4027                 val = superclass.doMethod.call(this, attr, start, end);
4028             }
4029             return val;
4030         };
4031
4032         proto.setRuntimeAttribute = function(attr) {
4033             if (this.patterns.points.test(attr)) {
4034                 var el = this.getEl();
4035                 var attributes = this.attributes;
4036                 var start;
4037                 var control = attributes['points']['control'] || [];
4038                 var end;
4039                 var i, len;
4040
4041                 if (control.length > 0 && !(control[0] instanceof Array)) {
4042                     control = [control];
4043                 } else {
4044                     var tmp = [];
4045                     for (i = 0,len = control.length; i < len; ++i) {
4046                         tmp[i] = control[i];
4047                     }
4048                     control = tmp;
4049                 }
4050
4051                 Roo.fly(el).position();
4052
4053                 if (isset(attributes['points']['from'])) {
4054                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4055                 }
4056                 else {
4057                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4058                 }
4059
4060                 start = this.getAttribute('points');
4061
4062
4063                 if (isset(attributes['points']['to'])) {
4064                     end = translateValues.call(this, attributes['points']['to'], start);
4065
4066                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4067                     for (i = 0,len = control.length; i < len; ++i) {
4068                         control[i] = translateValues.call(this, control[i], start);
4069                     }
4070
4071
4072                 } else if (isset(attributes['points']['by'])) {
4073                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4074
4075                     for (i = 0,len = control.length; i < len; ++i) {
4076                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4077                     }
4078                 }
4079
4080                 this.runtimeAttributes[attr] = [start];
4081
4082                 if (control.length > 0) {
4083                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4084                 }
4085
4086                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4087             }
4088             else {
4089                 superclass.setRuntimeAttribute.call(this, attr);
4090             }
4091         };
4092
4093         var translateValues = function(val, start) {
4094             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4095             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4096
4097             return val;
4098         };
4099
4100         var isset = function(prop) {
4101             return (typeof prop !== 'undefined');
4102         };
4103     })();
4104 /*
4105  * Portions of this file are based on pieces of Yahoo User Interface Library
4106  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4107  * YUI licensed under the BSD License:
4108  * http://developer.yahoo.net/yui/license.txt
4109  * <script type="text/javascript">
4110  *
4111  */
4112     (function() {
4113         Roo.lib.Scroll = function(el, attributes, duration, method) {
4114             if (el) {
4115                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4116             }
4117         };
4118
4119         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4120
4121
4122         var Y = Roo.lib;
4123         var superclass = Y.Scroll.superclass;
4124         var proto = Y.Scroll.prototype;
4125
4126         proto.toString = function() {
4127             var el = this.getEl();
4128             var id = el.id || el.tagName;
4129             return ("Scroll " + id);
4130         };
4131
4132         proto.doMethod = function(attr, start, end) {
4133             var val = null;
4134
4135             if (attr == 'scroll') {
4136                 val = [
4137                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4138                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4139                         ];
4140
4141             } else {
4142                 val = superclass.doMethod.call(this, attr, start, end);
4143             }
4144             return val;
4145         };
4146
4147         proto.getAttribute = function(attr) {
4148             var val = null;
4149             var el = this.getEl();
4150
4151             if (attr == 'scroll') {
4152                 val = [ el.scrollLeft, el.scrollTop ];
4153             } else {
4154                 val = superclass.getAttribute.call(this, attr);
4155             }
4156
4157             return val;
4158         };
4159
4160         proto.setAttribute = function(attr, val, unit) {
4161             var el = this.getEl();
4162
4163             if (attr == 'scroll') {
4164                 el.scrollLeft = val[0];
4165                 el.scrollTop = val[1];
4166             } else {
4167                 superclass.setAttribute.call(this, attr, val, unit);
4168             }
4169         };
4170     })();
4171 /*
4172  * Based on:
4173  * Ext JS Library 1.1.1
4174  * Copyright(c) 2006-2007, Ext JS, LLC.
4175  *
4176  * Originally Released Under LGPL - original licence link has changed is not relivant.
4177  *
4178  * Fork - LGPL
4179  * <script type="text/javascript">
4180  */
4181
4182
4183 // nasty IE9 hack - what a pile of crap that is..
4184
4185  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4186     Range.prototype.createContextualFragment = function (html) {
4187         var doc = window.document;
4188         var container = doc.createElement("div");
4189         container.innerHTML = html;
4190         var frag = doc.createDocumentFragment(), n;
4191         while ((n = container.firstChild)) {
4192             frag.appendChild(n);
4193         }
4194         return frag;
4195     };
4196 }
4197
4198 /**
4199  * @class Roo.DomHelper
4200  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4201  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4202  * @singleton
4203  */
4204 Roo.DomHelper = function(){
4205     var tempTableEl = null;
4206     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4207     var tableRe = /^table|tbody|tr|td$/i;
4208     var xmlns = {};
4209     // build as innerHTML where available
4210     /** @ignore */
4211     var createHtml = function(o){
4212         if(typeof o == 'string'){
4213             return o;
4214         }
4215         var b = "";
4216         if(!o.tag){
4217             o.tag = "div";
4218         }
4219         b += "<" + o.tag;
4220         for(var attr in o){
4221             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4222             if(attr == "style"){
4223                 var s = o["style"];
4224                 if(typeof s == "function"){
4225                     s = s.call();
4226                 }
4227                 if(typeof s == "string"){
4228                     b += ' style="' + s + '"';
4229                 }else if(typeof s == "object"){
4230                     b += ' style="';
4231                     for(var key in s){
4232                         if(typeof s[key] != "function"){
4233                             b += key + ":" + s[key] + ";";
4234                         }
4235                     }
4236                     b += '"';
4237                 }
4238             }else{
4239                 if(attr == "cls"){
4240                     b += ' class="' + o["cls"] + '"';
4241                 }else if(attr == "htmlFor"){
4242                     b += ' for="' + o["htmlFor"] + '"';
4243                 }else{
4244                     b += " " + attr + '="' + o[attr] + '"';
4245                 }
4246             }
4247         }
4248         if(emptyTags.test(o.tag)){
4249             b += "/>";
4250         }else{
4251             b += ">";
4252             var cn = o.children || o.cn;
4253             if(cn){
4254                 //http://bugs.kde.org/show_bug.cgi?id=71506
4255                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4256                     for(var i = 0, len = cn.length; i < len; i++) {
4257                         b += createHtml(cn[i], b);
4258                     }
4259                 }else{
4260                     b += createHtml(cn, b);
4261                 }
4262             }
4263             if(o.html){
4264                 b += o.html;
4265             }
4266             b += "</" + o.tag + ">";
4267         }
4268         return b;
4269     };
4270
4271     // build as dom
4272     /** @ignore */
4273     var createDom = function(o, parentNode){
4274          
4275         // defininition craeted..
4276         var ns = false;
4277         if (o.ns && o.ns != 'html') {
4278                
4279             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4280                 xmlns[o.ns] = o.xmlns;
4281                 ns = o.xmlns;
4282             }
4283             if (typeof(xmlns[o.ns]) == 'undefined') {
4284                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4285             }
4286             ns = xmlns[o.ns];
4287         }
4288         
4289         
4290         if (typeof(o) == 'string') {
4291             return parentNode.appendChild(document.createTextNode(o));
4292         }
4293         o.tag = o.tag || div;
4294         if (o.ns && Roo.isIE) {
4295             ns = false;
4296             o.tag = o.ns + ':' + o.tag;
4297             
4298         }
4299         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4300         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4301         for(var attr in o){
4302             
4303             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4304                     attr == "style" || typeof o[attr] == "function") { continue; }
4305                     
4306             if(attr=="cls" && Roo.isIE){
4307                 el.className = o["cls"];
4308             }else{
4309                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4310                 else { 
4311                     el[attr] = o[attr];
4312                 }
4313             }
4314         }
4315         Roo.DomHelper.applyStyles(el, o.style);
4316         var cn = o.children || o.cn;
4317         if(cn){
4318             //http://bugs.kde.org/show_bug.cgi?id=71506
4319              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4320                 for(var i = 0, len = cn.length; i < len; i++) {
4321                     createDom(cn[i], el);
4322                 }
4323             }else{
4324                 createDom(cn, el);
4325             }
4326         }
4327         if(o.html){
4328             el.innerHTML = o.html;
4329         }
4330         if(parentNode){
4331            parentNode.appendChild(el);
4332         }
4333         return el;
4334     };
4335
4336     var ieTable = function(depth, s, h, e){
4337         tempTableEl.innerHTML = [s, h, e].join('');
4338         var i = -1, el = tempTableEl;
4339         while(++i < depth){
4340             el = el.firstChild;
4341         }
4342         return el;
4343     };
4344
4345     // kill repeat to save bytes
4346     var ts = '<table>',
4347         te = '</table>',
4348         tbs = ts+'<tbody>',
4349         tbe = '</tbody>'+te,
4350         trs = tbs + '<tr>',
4351         tre = '</tr>'+tbe;
4352
4353     /**
4354      * @ignore
4355      * Nasty code for IE's broken table implementation
4356      */
4357     var insertIntoTable = function(tag, where, el, html){
4358         if(!tempTableEl){
4359             tempTableEl = document.createElement('div');
4360         }
4361         var node;
4362         var before = null;
4363         if(tag == 'td'){
4364             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4365                 return;
4366             }
4367             if(where == 'beforebegin'){
4368                 before = el;
4369                 el = el.parentNode;
4370             } else{
4371                 before = el.nextSibling;
4372                 el = el.parentNode;
4373             }
4374             node = ieTable(4, trs, html, tre);
4375         }
4376         else if(tag == 'tr'){
4377             if(where == 'beforebegin'){
4378                 before = el;
4379                 el = el.parentNode;
4380                 node = ieTable(3, tbs, html, tbe);
4381             } else if(where == 'afterend'){
4382                 before = el.nextSibling;
4383                 el = el.parentNode;
4384                 node = ieTable(3, tbs, html, tbe);
4385             } else{ // INTO a TR
4386                 if(where == 'afterbegin'){
4387                     before = el.firstChild;
4388                 }
4389                 node = ieTable(4, trs, html, tre);
4390             }
4391         } else if(tag == 'tbody'){
4392             if(where == 'beforebegin'){
4393                 before = el;
4394                 el = el.parentNode;
4395                 node = ieTable(2, ts, html, te);
4396             } else if(where == 'afterend'){
4397                 before = el.nextSibling;
4398                 el = el.parentNode;
4399                 node = ieTable(2, ts, html, te);
4400             } else{
4401                 if(where == 'afterbegin'){
4402                     before = el.firstChild;
4403                 }
4404                 node = ieTable(3, tbs, html, tbe);
4405             }
4406         } else{ // TABLE
4407             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4408                 return;
4409             }
4410             if(where == 'afterbegin'){
4411                 before = el.firstChild;
4412             }
4413             node = ieTable(2, ts, html, te);
4414         }
4415         el.insertBefore(node, before);
4416         return node;
4417     };
4418
4419     return {
4420     /** True to force the use of DOM instead of html fragments @type Boolean */
4421     useDom : false,
4422
4423     /**
4424      * Returns the markup for the passed Element(s) config
4425      * @param {Object} o The Dom object spec (and children)
4426      * @return {String}
4427      */
4428     markup : function(o){
4429         return createHtml(o);
4430     },
4431
4432     /**
4433      * Applies a style specification to an element
4434      * @param {String/HTMLElement} el The element to apply styles to
4435      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4436      * a function which returns such a specification.
4437      */
4438     applyStyles : function(el, styles){
4439         if(styles){
4440            el = Roo.fly(el);
4441            if(typeof styles == "string"){
4442                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4443                var matches;
4444                while ((matches = re.exec(styles)) != null){
4445                    el.setStyle(matches[1], matches[2]);
4446                }
4447            }else if (typeof styles == "object"){
4448                for (var style in styles){
4449                   el.setStyle(style, styles[style]);
4450                }
4451            }else if (typeof styles == "function"){
4452                 Roo.DomHelper.applyStyles(el, styles.call());
4453            }
4454         }
4455     },
4456
4457     /**
4458      * Inserts an HTML fragment into the Dom
4459      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4460      * @param {HTMLElement} el The context element
4461      * @param {String} html The HTML fragmenet
4462      * @return {HTMLElement} The new node
4463      */
4464     insertHtml : function(where, el, html){
4465         where = where.toLowerCase();
4466         if(el.insertAdjacentHTML){
4467             if(tableRe.test(el.tagName)){
4468                 var rs;
4469                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4470                     return rs;
4471                 }
4472             }
4473             switch(where){
4474                 case "beforebegin":
4475                     el.insertAdjacentHTML('BeforeBegin', html);
4476                     return el.previousSibling;
4477                 case "afterbegin":
4478                     el.insertAdjacentHTML('AfterBegin', html);
4479                     return el.firstChild;
4480                 case "beforeend":
4481                     el.insertAdjacentHTML('BeforeEnd', html);
4482                     return el.lastChild;
4483                 case "afterend":
4484                     el.insertAdjacentHTML('AfterEnd', html);
4485                     return el.nextSibling;
4486             }
4487             throw 'Illegal insertion point -> "' + where + '"';
4488         }
4489         var range = el.ownerDocument.createRange();
4490         var frag;
4491         switch(where){
4492              case "beforebegin":
4493                 range.setStartBefore(el);
4494                 frag = range.createContextualFragment(html);
4495                 el.parentNode.insertBefore(frag, el);
4496                 return el.previousSibling;
4497              case "afterbegin":
4498                 if(el.firstChild){
4499                     range.setStartBefore(el.firstChild);
4500                     frag = range.createContextualFragment(html);
4501                     el.insertBefore(frag, el.firstChild);
4502                     return el.firstChild;
4503                 }else{
4504                     el.innerHTML = html;
4505                     return el.firstChild;
4506                 }
4507             case "beforeend":
4508                 if(el.lastChild){
4509                     range.setStartAfter(el.lastChild);
4510                     frag = range.createContextualFragment(html);
4511                     el.appendChild(frag);
4512                     return el.lastChild;
4513                 }else{
4514                     el.innerHTML = html;
4515                     return el.lastChild;
4516                 }
4517             case "afterend":
4518                 range.setStartAfter(el);
4519                 frag = range.createContextualFragment(html);
4520                 el.parentNode.insertBefore(frag, el.nextSibling);
4521                 return el.nextSibling;
4522             }
4523             throw 'Illegal insertion point -> "' + where + '"';
4524     },
4525
4526     /**
4527      * Creates new Dom element(s) and inserts them before el
4528      * @param {String/HTMLElement/Element} el The context element
4529      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4530      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4531      * @return {HTMLElement/Roo.Element} The new node
4532      */
4533     insertBefore : function(el, o, returnElement){
4534         return this.doInsert(el, o, returnElement, "beforeBegin");
4535     },
4536
4537     /**
4538      * Creates new Dom element(s) and inserts them after el
4539      * @param {String/HTMLElement/Element} el The context element
4540      * @param {Object} o The Dom object spec (and children)
4541      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4542      * @return {HTMLElement/Roo.Element} The new node
4543      */
4544     insertAfter : function(el, o, returnElement){
4545         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4546     },
4547
4548     /**
4549      * Creates new Dom element(s) and inserts them as the first child of el
4550      * @param {String/HTMLElement/Element} el The context element
4551      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4552      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4553      * @return {HTMLElement/Roo.Element} The new node
4554      */
4555     insertFirst : function(el, o, returnElement){
4556         return this.doInsert(el, o, returnElement, "afterBegin");
4557     },
4558
4559     // private
4560     doInsert : function(el, o, returnElement, pos, sibling){
4561         el = Roo.getDom(el);
4562         var newNode;
4563         if(this.useDom || o.ns){
4564             newNode = createDom(o, null);
4565             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4566         }else{
4567             var html = createHtml(o);
4568             newNode = this.insertHtml(pos, el, html);
4569         }
4570         return returnElement ? Roo.get(newNode, true) : newNode;
4571     },
4572
4573     /**
4574      * Creates new Dom element(s) and appends them to el
4575      * @param {String/HTMLElement/Element} el The context element
4576      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4577      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4578      * @return {HTMLElement/Roo.Element} The new node
4579      */
4580     append : function(el, o, returnElement){
4581         el = Roo.getDom(el);
4582         var newNode;
4583         if(this.useDom || o.ns){
4584             newNode = createDom(o, null);
4585             el.appendChild(newNode);
4586         }else{
4587             var html = createHtml(o);
4588             newNode = this.insertHtml("beforeEnd", el, html);
4589         }
4590         return returnElement ? Roo.get(newNode, true) : newNode;
4591     },
4592
4593     /**
4594      * Creates new Dom element(s) and overwrites the contents of el with them
4595      * @param {String/HTMLElement/Element} el The context element
4596      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4597      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4598      * @return {HTMLElement/Roo.Element} The new node
4599      */
4600     overwrite : function(el, o, returnElement){
4601         el = Roo.getDom(el);
4602         if (o.ns) {
4603           
4604             while (el.childNodes.length) {
4605                 el.removeChild(el.firstChild);
4606             }
4607             createDom(o, el);
4608         } else {
4609             el.innerHTML = createHtml(o);   
4610         }
4611         
4612         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4613     },
4614
4615     /**
4616      * Creates a new Roo.DomHelper.Template from the Dom object spec
4617      * @param {Object} o The Dom object spec (and children)
4618      * @return {Roo.DomHelper.Template} The new template
4619      */
4620     createTemplate : function(o){
4621         var html = createHtml(o);
4622         return new Roo.Template(html);
4623     }
4624     };
4625 }();
4626 /*
4627  * Based on:
4628  * Ext JS Library 1.1.1
4629  * Copyright(c) 2006-2007, Ext JS, LLC.
4630  *
4631  * Originally Released Under LGPL - original licence link has changed is not relivant.
4632  *
4633  * Fork - LGPL
4634  * <script type="text/javascript">
4635  */
4636  
4637 /**
4638 * @class Roo.Template
4639 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4640 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4641 * Usage:
4642 <pre><code>
4643 var t = new Roo.Template({
4644     html :  '&lt;div name="{id}"&gt;' + 
4645         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4646         '&lt;/div&gt;',
4647     myformat: function (value, allValues) {
4648         return 'XX' + value;
4649     }
4650 });
4651 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4652 </code></pre>
4653 * For more information see this blog post with examples:
4654 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4655      - Create Elements using DOM, HTML fragments and Templates</a>. 
4656 * @constructor
4657 * @param {Object} cfg - Configuration object.
4658 */
4659 Roo.Template = function(cfg){
4660     // BC!
4661     if(cfg instanceof Array){
4662         cfg = cfg.join("");
4663     }else if(arguments.length > 1){
4664         cfg = Array.prototype.join.call(arguments, "");
4665     }
4666     
4667     
4668     if (typeof(cfg) == 'object') {
4669         Roo.apply(this,cfg)
4670     } else {
4671         // bc
4672         this.html = cfg;
4673     }
4674     if (this.url) {
4675         this.load();
4676     }
4677     
4678 };
4679 Roo.Template.prototype = {
4680     
4681     /**
4682      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4683      */
4684     onLoad : false,
4685     
4686     
4687     /**
4688      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4689      *                    it should be fixed so that template is observable...
4690      */
4691     url : false,
4692     /**
4693      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4694      */
4695     html : '',
4696     
4697     
4698     compiled : false,
4699     loaded : false,
4700     /**
4701      * Returns an HTML fragment of this template with the specified values applied.
4702      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4703      * @return {String} The HTML fragment
4704      */
4705     
4706    
4707     
4708     applyTemplate : function(values){
4709         //Roo.log(["applyTemplate", values]);
4710         try {
4711            
4712             if(this.compiled){
4713                 return this.compiled(values);
4714             }
4715             var useF = this.disableFormats !== true;
4716             var fm = Roo.util.Format, tpl = this;
4717             var fn = function(m, name, format, args){
4718                 if(format && useF){
4719                     if(format.substr(0, 5) == "this."){
4720                         return tpl.call(format.substr(5), values[name], values);
4721                     }else{
4722                         if(args){
4723                             // quoted values are required for strings in compiled templates, 
4724                             // but for non compiled we need to strip them
4725                             // quoted reversed for jsmin
4726                             var re = /^\s*['"](.*)["']\s*$/;
4727                             args = args.split(',');
4728                             for(var i = 0, len = args.length; i < len; i++){
4729                                 args[i] = args[i].replace(re, "$1");
4730                             }
4731                             args = [values[name]].concat(args);
4732                         }else{
4733                             args = [values[name]];
4734                         }
4735                         return fm[format].apply(fm, args);
4736                     }
4737                 }else{
4738                     return values[name] !== undefined ? values[name] : "";
4739                 }
4740             };
4741             return this.html.replace(this.re, fn);
4742         } catch (e) {
4743             Roo.log(e);
4744             throw e;
4745         }
4746          
4747     },
4748     
4749     loading : false,
4750       
4751     load : function ()
4752     {
4753          
4754         if (this.loading) {
4755             return;
4756         }
4757         var _t = this;
4758         
4759         this.loading = true;
4760         this.compiled = false;
4761         
4762         var cx = new Roo.data.Connection();
4763         cx.request({
4764             url : this.url,
4765             method : 'GET',
4766             success : function (response) {
4767                 _t.loading = false;
4768                 _t.url = false;
4769                 
4770                 _t.set(response.responseText,true);
4771                 _t.loaded = true;
4772                 if (_t.onLoad) {
4773                     _t.onLoad();
4774                 }
4775              },
4776             failure : function(response) {
4777                 Roo.log("Template failed to load from " + _t.url);
4778                 _t.loading = false;
4779             }
4780         });
4781     },
4782
4783     /**
4784      * Sets the HTML used as the template and optionally compiles it.
4785      * @param {String} html
4786      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4787      * @return {Roo.Template} this
4788      */
4789     set : function(html, compile){
4790         this.html = html;
4791         this.compiled = false;
4792         if(compile){
4793             this.compile();
4794         }
4795         return this;
4796     },
4797     
4798     /**
4799      * True to disable format functions (defaults to false)
4800      * @type Boolean
4801      */
4802     disableFormats : false,
4803     
4804     /**
4805     * The regular expression used to match template variables 
4806     * @type RegExp
4807     * @property 
4808     */
4809     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4810     
4811     /**
4812      * Compiles the template into an internal function, eliminating the RegEx overhead.
4813      * @return {Roo.Template} this
4814      */
4815     compile : function(){
4816         var fm = Roo.util.Format;
4817         var useF = this.disableFormats !== true;
4818         var sep = Roo.isGecko ? "+" : ",";
4819         var fn = function(m, name, format, args){
4820             if(format && useF){
4821                 args = args ? ',' + args : "";
4822                 if(format.substr(0, 5) != "this."){
4823                     format = "fm." + format + '(';
4824                 }else{
4825                     format = 'this.call("'+ format.substr(5) + '", ';
4826                     args = ", values";
4827                 }
4828             }else{
4829                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4830             }
4831             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4832         };
4833         var body;
4834         // branched to use + in gecko and [].join() in others
4835         if(Roo.isGecko){
4836             body = "this.compiled = function(values){ return '" +
4837                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4838                     "';};";
4839         }else{
4840             body = ["this.compiled = function(values){ return ['"];
4841             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4842             body.push("'].join('');};");
4843             body = body.join('');
4844         }
4845         /**
4846          * eval:var:values
4847          * eval:var:fm
4848          */
4849         eval(body);
4850         return this;
4851     },
4852     
4853     // private function used to call members
4854     call : function(fnName, value, allValues){
4855         return this[fnName](value, allValues);
4856     },
4857     
4858     /**
4859      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4860      * @param {String/HTMLElement/Roo.Element} el The context element
4861      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4862      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4863      * @return {HTMLElement/Roo.Element} The new node or Element
4864      */
4865     insertFirst: function(el, values, returnElement){
4866         return this.doInsert('afterBegin', el, values, returnElement);
4867     },
4868
4869     /**
4870      * Applies the supplied values to the template and inserts the new node(s) before el.
4871      * @param {String/HTMLElement/Roo.Element} el The context element
4872      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4873      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4874      * @return {HTMLElement/Roo.Element} The new node or Element
4875      */
4876     insertBefore: function(el, values, returnElement){
4877         return this.doInsert('beforeBegin', el, values, returnElement);
4878     },
4879
4880     /**
4881      * Applies the supplied values to the template and inserts the new node(s) after el.
4882      * @param {String/HTMLElement/Roo.Element} el The context element
4883      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4884      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4885      * @return {HTMLElement/Roo.Element} The new node or Element
4886      */
4887     insertAfter : function(el, values, returnElement){
4888         return this.doInsert('afterEnd', el, values, returnElement);
4889     },
4890     
4891     /**
4892      * Applies the supplied values to the template and appends the new node(s) to el.
4893      * @param {String/HTMLElement/Roo.Element} el The context element
4894      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4895      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4896      * @return {HTMLElement/Roo.Element} The new node or Element
4897      */
4898     append : function(el, values, returnElement){
4899         return this.doInsert('beforeEnd', el, values, returnElement);
4900     },
4901
4902     doInsert : function(where, el, values, returnEl){
4903         el = Roo.getDom(el);
4904         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4905         return returnEl ? Roo.get(newNode, true) : newNode;
4906     },
4907
4908     /**
4909      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4910      * @param {String/HTMLElement/Roo.Element} el The context element
4911      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4912      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4913      * @return {HTMLElement/Roo.Element} The new node or Element
4914      */
4915     overwrite : function(el, values, returnElement){
4916         el = Roo.getDom(el);
4917         el.innerHTML = this.applyTemplate(values);
4918         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4919     }
4920 };
4921 /**
4922  * Alias for {@link #applyTemplate}
4923  * @method
4924  */
4925 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4926
4927 // backwards compat
4928 Roo.DomHelper.Template = Roo.Template;
4929
4930 /**
4931  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4932  * @param {String/HTMLElement} el A DOM element or its id
4933  * @returns {Roo.Template} The created template
4934  * @static
4935  */
4936 Roo.Template.from = function(el){
4937     el = Roo.getDom(el);
4938     return new Roo.Template(el.value || el.innerHTML);
4939 };/*
4940  * Based on:
4941  * Ext JS Library 1.1.1
4942  * Copyright(c) 2006-2007, Ext JS, LLC.
4943  *
4944  * Originally Released Under LGPL - original licence link has changed is not relivant.
4945  *
4946  * Fork - LGPL
4947  * <script type="text/javascript">
4948  */
4949  
4950
4951 /*
4952  * This is code is also distributed under MIT license for use
4953  * with jQuery and prototype JavaScript libraries.
4954  */
4955 /**
4956  * @class Roo.DomQuery
4957 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4958 <p>
4959 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4960
4961 <p>
4962 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4963 </p>
4964 <h4>Element Selectors:</h4>
4965 <ul class="list">
4966     <li> <b>*</b> any element</li>
4967     <li> <b>E</b> an element with the tag E</li>
4968     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4969     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4970     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4971     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4972 </ul>
4973 <h4>Attribute Selectors:</h4>
4974 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4975 <ul class="list">
4976     <li> <b>E[foo]</b> has an attribute "foo"</li>
4977     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4978     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4979     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4980     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4981     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4982     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4983 </ul>
4984 <h4>Pseudo Classes:</h4>
4985 <ul class="list">
4986     <li> <b>E:first-child</b> E is the first child of its parent</li>
4987     <li> <b>E:last-child</b> E is the last child of its parent</li>
4988     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4989     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4990     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4991     <li> <b>E:only-child</b> E is the only child of its parent</li>
4992     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4993     <li> <b>E:first</b> the first E in the resultset</li>
4994     <li> <b>E:last</b> the last E in the resultset</li>
4995     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4996     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4997     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4998     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4999     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5000     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5001     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5002     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5003     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5004 </ul>
5005 <h4>CSS Value Selectors:</h4>
5006 <ul class="list">
5007     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5008     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5009     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5010     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5011     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5012     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5013 </ul>
5014  * @singleton
5015  */
5016 Roo.DomQuery = function(){
5017     var cache = {}, simpleCache = {}, valueCache = {};
5018     var nonSpace = /\S/;
5019     var trimRe = /^\s+|\s+$/g;
5020     var tplRe = /\{(\d+)\}/g;
5021     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5022     var tagTokenRe = /^(#)?([\w-\*]+)/;
5023     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5024
5025     function child(p, index){
5026         var i = 0;
5027         var n = p.firstChild;
5028         while(n){
5029             if(n.nodeType == 1){
5030                if(++i == index){
5031                    return n;
5032                }
5033             }
5034             n = n.nextSibling;
5035         }
5036         return null;
5037     };
5038
5039     function next(n){
5040         while((n = n.nextSibling) && n.nodeType != 1);
5041         return n;
5042     };
5043
5044     function prev(n){
5045         while((n = n.previousSibling) && n.nodeType != 1);
5046         return n;
5047     };
5048
5049     function children(d){
5050         var n = d.firstChild, ni = -1;
5051             while(n){
5052                 var nx = n.nextSibling;
5053                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5054                     d.removeChild(n);
5055                 }else{
5056                     n.nodeIndex = ++ni;
5057                 }
5058                 n = nx;
5059             }
5060             return this;
5061         };
5062
5063     function byClassName(c, a, v){
5064         if(!v){
5065             return c;
5066         }
5067         var r = [], ri = -1, cn;
5068         for(var i = 0, ci; ci = c[i]; i++){
5069             if((' '+ci.className+' ').indexOf(v) != -1){
5070                 r[++ri] = ci;
5071             }
5072         }
5073         return r;
5074     };
5075
5076     function attrValue(n, attr){
5077         if(!n.tagName && typeof n.length != "undefined"){
5078             n = n[0];
5079         }
5080         if(!n){
5081             return null;
5082         }
5083         if(attr == "for"){
5084             return n.htmlFor;
5085         }
5086         if(attr == "class" || attr == "className"){
5087             return n.className;
5088         }
5089         return n.getAttribute(attr) || n[attr];
5090
5091     };
5092
5093     function getNodes(ns, mode, tagName){
5094         var result = [], ri = -1, cs;
5095         if(!ns){
5096             return result;
5097         }
5098         tagName = tagName || "*";
5099         if(typeof ns.getElementsByTagName != "undefined"){
5100             ns = [ns];
5101         }
5102         if(!mode){
5103             for(var i = 0, ni; ni = ns[i]; i++){
5104                 cs = ni.getElementsByTagName(tagName);
5105                 for(var j = 0, ci; ci = cs[j]; j++){
5106                     result[++ri] = ci;
5107                 }
5108             }
5109         }else if(mode == "/" || mode == ">"){
5110             var utag = tagName.toUpperCase();
5111             for(var i = 0, ni, cn; ni = ns[i]; i++){
5112                 cn = ni.children || ni.childNodes;
5113                 for(var j = 0, cj; cj = cn[j]; j++){
5114                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5115                         result[++ri] = cj;
5116                     }
5117                 }
5118             }
5119         }else if(mode == "+"){
5120             var utag = tagName.toUpperCase();
5121             for(var i = 0, n; n = ns[i]; i++){
5122                 while((n = n.nextSibling) && n.nodeType != 1);
5123                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5124                     result[++ri] = n;
5125                 }
5126             }
5127         }else if(mode == "~"){
5128             for(var i = 0, n; n = ns[i]; i++){
5129                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5130                 if(n){
5131                     result[++ri] = n;
5132                 }
5133             }
5134         }
5135         return result;
5136     };
5137
5138     function concat(a, b){
5139         if(b.slice){
5140             return a.concat(b);
5141         }
5142         for(var i = 0, l = b.length; i < l; i++){
5143             a[a.length] = b[i];
5144         }
5145         return a;
5146     }
5147
5148     function byTag(cs, tagName){
5149         if(cs.tagName || cs == document){
5150             cs = [cs];
5151         }
5152         if(!tagName){
5153             return cs;
5154         }
5155         var r = [], ri = -1;
5156         tagName = tagName.toLowerCase();
5157         for(var i = 0, ci; ci = cs[i]; i++){
5158             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5159                 r[++ri] = ci;
5160             }
5161         }
5162         return r;
5163     };
5164
5165     function byId(cs, attr, id){
5166         if(cs.tagName || cs == document){
5167             cs = [cs];
5168         }
5169         if(!id){
5170             return cs;
5171         }
5172         var r = [], ri = -1;
5173         for(var i = 0,ci; ci = cs[i]; i++){
5174             if(ci && ci.id == id){
5175                 r[++ri] = ci;
5176                 return r;
5177             }
5178         }
5179         return r;
5180     };
5181
5182     function byAttribute(cs, attr, value, op, custom){
5183         var r = [], ri = -1, st = custom=="{";
5184         var f = Roo.DomQuery.operators[op];
5185         for(var i = 0, ci; ci = cs[i]; i++){
5186             var a;
5187             if(st){
5188                 a = Roo.DomQuery.getStyle(ci, attr);
5189             }
5190             else if(attr == "class" || attr == "className"){
5191                 a = ci.className;
5192             }else if(attr == "for"){
5193                 a = ci.htmlFor;
5194             }else if(attr == "href"){
5195                 a = ci.getAttribute("href", 2);
5196             }else{
5197                 a = ci.getAttribute(attr);
5198             }
5199             if((f && f(a, value)) || (!f && a)){
5200                 r[++ri] = ci;
5201             }
5202         }
5203         return r;
5204     };
5205
5206     function byPseudo(cs, name, value){
5207         return Roo.DomQuery.pseudos[name](cs, value);
5208     };
5209
5210     // This is for IE MSXML which does not support expandos.
5211     // IE runs the same speed using setAttribute, however FF slows way down
5212     // and Safari completely fails so they need to continue to use expandos.
5213     var isIE = window.ActiveXObject ? true : false;
5214
5215     // this eval is stop the compressor from
5216     // renaming the variable to something shorter
5217     
5218     /** eval:var:batch */
5219     var batch = 30803; 
5220
5221     var key = 30803;
5222
5223     function nodupIEXml(cs){
5224         var d = ++key;
5225         cs[0].setAttribute("_nodup", d);
5226         var r = [cs[0]];
5227         for(var i = 1, len = cs.length; i < len; i++){
5228             var c = cs[i];
5229             if(!c.getAttribute("_nodup") != d){
5230                 c.setAttribute("_nodup", d);
5231                 r[r.length] = c;
5232             }
5233         }
5234         for(var i = 0, len = cs.length; i < len; i++){
5235             cs[i].removeAttribute("_nodup");
5236         }
5237         return r;
5238     }
5239
5240     function nodup(cs){
5241         if(!cs){
5242             return [];
5243         }
5244         var len = cs.length, c, i, r = cs, cj, ri = -1;
5245         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5246             return cs;
5247         }
5248         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5249             return nodupIEXml(cs);
5250         }
5251         var d = ++key;
5252         cs[0]._nodup = d;
5253         for(i = 1; c = cs[i]; i++){
5254             if(c._nodup != d){
5255                 c._nodup = d;
5256             }else{
5257                 r = [];
5258                 for(var j = 0; j < i; j++){
5259                     r[++ri] = cs[j];
5260                 }
5261                 for(j = i+1; cj = cs[j]; j++){
5262                     if(cj._nodup != d){
5263                         cj._nodup = d;
5264                         r[++ri] = cj;
5265                     }
5266                 }
5267                 return r;
5268             }
5269         }
5270         return r;
5271     }
5272
5273     function quickDiffIEXml(c1, c2){
5274         var d = ++key;
5275         for(var i = 0, len = c1.length; i < len; i++){
5276             c1[i].setAttribute("_qdiff", d);
5277         }
5278         var r = [];
5279         for(var i = 0, len = c2.length; i < len; i++){
5280             if(c2[i].getAttribute("_qdiff") != d){
5281                 r[r.length] = c2[i];
5282             }
5283         }
5284         for(var i = 0, len = c1.length; i < len; i++){
5285            c1[i].removeAttribute("_qdiff");
5286         }
5287         return r;
5288     }
5289
5290     function quickDiff(c1, c2){
5291         var len1 = c1.length;
5292         if(!len1){
5293             return c2;
5294         }
5295         if(isIE && c1[0].selectSingleNode){
5296             return quickDiffIEXml(c1, c2);
5297         }
5298         var d = ++key;
5299         for(var i = 0; i < len1; i++){
5300             c1[i]._qdiff = d;
5301         }
5302         var r = [];
5303         for(var i = 0, len = c2.length; i < len; i++){
5304             if(c2[i]._qdiff != d){
5305                 r[r.length] = c2[i];
5306             }
5307         }
5308         return r;
5309     }
5310
5311     function quickId(ns, mode, root, id){
5312         if(ns == root){
5313            var d = root.ownerDocument || root;
5314            return d.getElementById(id);
5315         }
5316         ns = getNodes(ns, mode, "*");
5317         return byId(ns, null, id);
5318     }
5319
5320     return {
5321         getStyle : function(el, name){
5322             return Roo.fly(el).getStyle(name);
5323         },
5324         /**
5325          * Compiles a selector/xpath query into a reusable function. The returned function
5326          * takes one parameter "root" (optional), which is the context node from where the query should start.
5327          * @param {String} selector The selector/xpath query
5328          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5329          * @return {Function}
5330          */
5331         compile : function(path, type){
5332             type = type || "select";
5333             
5334             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5335             var q = path, mode, lq;
5336             var tk = Roo.DomQuery.matchers;
5337             var tklen = tk.length;
5338             var mm;
5339
5340             // accept leading mode switch
5341             var lmode = q.match(modeRe);
5342             if(lmode && lmode[1]){
5343                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5344                 q = q.replace(lmode[1], "");
5345             }
5346             // strip leading slashes
5347             while(path.substr(0, 1)=="/"){
5348                 path = path.substr(1);
5349             }
5350
5351             while(q && lq != q){
5352                 lq = q;
5353                 var tm = q.match(tagTokenRe);
5354                 if(type == "select"){
5355                     if(tm){
5356                         if(tm[1] == "#"){
5357                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5358                         }else{
5359                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5360                         }
5361                         q = q.replace(tm[0], "");
5362                     }else if(q.substr(0, 1) != '@'){
5363                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5364                     }
5365                 }else{
5366                     if(tm){
5367                         if(tm[1] == "#"){
5368                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5369                         }else{
5370                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5371                         }
5372                         q = q.replace(tm[0], "");
5373                     }
5374                 }
5375                 while(!(mm = q.match(modeRe))){
5376                     var matched = false;
5377                     for(var j = 0; j < tklen; j++){
5378                         var t = tk[j];
5379                         var m = q.match(t.re);
5380                         if(m){
5381                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5382                                                     return m[i];
5383                                                 });
5384                             q = q.replace(m[0], "");
5385                             matched = true;
5386                             break;
5387                         }
5388                     }
5389                     // prevent infinite loop on bad selector
5390                     if(!matched){
5391                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5392                     }
5393                 }
5394                 if(mm[1]){
5395                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5396                     q = q.replace(mm[1], "");
5397                 }
5398             }
5399             fn[fn.length] = "return nodup(n);\n}";
5400             
5401              /** 
5402               * list of variables that need from compression as they are used by eval.
5403              *  eval:var:batch 
5404              *  eval:var:nodup
5405              *  eval:var:byTag
5406              *  eval:var:ById
5407              *  eval:var:getNodes
5408              *  eval:var:quickId
5409              *  eval:var:mode
5410              *  eval:var:root
5411              *  eval:var:n
5412              *  eval:var:byClassName
5413              *  eval:var:byPseudo
5414              *  eval:var:byAttribute
5415              *  eval:var:attrValue
5416              * 
5417              **/ 
5418             eval(fn.join(""));
5419             return f;
5420         },
5421
5422         /**
5423          * Selects a group of elements.
5424          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5425          * @param {Node} root (optional) The start of the query (defaults to document).
5426          * @return {Array}
5427          */
5428         select : function(path, root, type){
5429             if(!root || root == document){
5430                 root = document;
5431             }
5432             if(typeof root == "string"){
5433                 root = document.getElementById(root);
5434             }
5435             var paths = path.split(",");
5436             var results = [];
5437             for(var i = 0, len = paths.length; i < len; i++){
5438                 var p = paths[i].replace(trimRe, "");
5439                 if(!cache[p]){
5440                     cache[p] = Roo.DomQuery.compile(p);
5441                     if(!cache[p]){
5442                         throw p + " is not a valid selector";
5443                     }
5444                 }
5445                 var result = cache[p](root);
5446                 if(result && result != document){
5447                     results = results.concat(result);
5448                 }
5449             }
5450             if(paths.length > 1){
5451                 return nodup(results);
5452             }
5453             return results;
5454         },
5455
5456         /**
5457          * Selects a single element.
5458          * @param {String} selector The selector/xpath query
5459          * @param {Node} root (optional) The start of the query (defaults to document).
5460          * @return {Element}
5461          */
5462         selectNode : function(path, root){
5463             return Roo.DomQuery.select(path, root)[0];
5464         },
5465
5466         /**
5467          * Selects the value of a node, optionally replacing null with the defaultValue.
5468          * @param {String} selector The selector/xpath query
5469          * @param {Node} root (optional) The start of the query (defaults to document).
5470          * @param {String} defaultValue
5471          */
5472         selectValue : function(path, root, defaultValue){
5473             path = path.replace(trimRe, "");
5474             if(!valueCache[path]){
5475                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5476             }
5477             var n = valueCache[path](root);
5478             n = n[0] ? n[0] : n;
5479             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5480             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5481         },
5482
5483         /**
5484          * Selects the value of a node, parsing integers and floats.
5485          * @param {String} selector The selector/xpath query
5486          * @param {Node} root (optional) The start of the query (defaults to document).
5487          * @param {Number} defaultValue
5488          * @return {Number}
5489          */
5490         selectNumber : function(path, root, defaultValue){
5491             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5492             return parseFloat(v);
5493         },
5494
5495         /**
5496          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5497          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5498          * @param {String} selector The simple selector to test
5499          * @return {Boolean}
5500          */
5501         is : function(el, ss){
5502             if(typeof el == "string"){
5503                 el = document.getElementById(el);
5504             }
5505             var isArray = (el instanceof Array);
5506             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5507             return isArray ? (result.length == el.length) : (result.length > 0);
5508         },
5509
5510         /**
5511          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5512          * @param {Array} el An array of elements to filter
5513          * @param {String} selector The simple selector to test
5514          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5515          * the selector instead of the ones that match
5516          * @return {Array}
5517          */
5518         filter : function(els, ss, nonMatches){
5519             ss = ss.replace(trimRe, "");
5520             if(!simpleCache[ss]){
5521                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5522             }
5523             var result = simpleCache[ss](els);
5524             return nonMatches ? quickDiff(result, els) : result;
5525         },
5526
5527         /**
5528          * Collection of matching regular expressions and code snippets.
5529          */
5530         matchers : [{
5531                 re: /^\.([\w-]+)/,
5532                 select: 'n = byClassName(n, null, " {1} ");'
5533             }, {
5534                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5535                 select: 'n = byPseudo(n, "{1}", "{2}");'
5536             },{
5537                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5538                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5539             }, {
5540                 re: /^#([\w-]+)/,
5541                 select: 'n = byId(n, null, "{1}");'
5542             },{
5543                 re: /^@([\w-]+)/,
5544                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5545             }
5546         ],
5547
5548         /**
5549          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5550          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5551          */
5552         operators : {
5553             "=" : function(a, v){
5554                 return a == v;
5555             },
5556             "!=" : function(a, v){
5557                 return a != v;
5558             },
5559             "^=" : function(a, v){
5560                 return a && a.substr(0, v.length) == v;
5561             },
5562             "$=" : function(a, v){
5563                 return a && a.substr(a.length-v.length) == v;
5564             },
5565             "*=" : function(a, v){
5566                 return a && a.indexOf(v) !== -1;
5567             },
5568             "%=" : function(a, v){
5569                 return (a % v) == 0;
5570             },
5571             "|=" : function(a, v){
5572                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5573             },
5574             "~=" : function(a, v){
5575                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5576             }
5577         },
5578
5579         /**
5580          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5581          * and the argument (if any) supplied in the selector.
5582          */
5583         pseudos : {
5584             "first-child" : function(c){
5585                 var r = [], ri = -1, n;
5586                 for(var i = 0, ci; ci = n = c[i]; i++){
5587                     while((n = n.previousSibling) && n.nodeType != 1);
5588                     if(!n){
5589                         r[++ri] = ci;
5590                     }
5591                 }
5592                 return r;
5593             },
5594
5595             "last-child" : function(c){
5596                 var r = [], ri = -1, n;
5597                 for(var i = 0, ci; ci = n = c[i]; i++){
5598                     while((n = n.nextSibling) && n.nodeType != 1);
5599                     if(!n){
5600                         r[++ri] = ci;
5601                     }
5602                 }
5603                 return r;
5604             },
5605
5606             "nth-child" : function(c, a) {
5607                 var r = [], ri = -1;
5608                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5609                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5610                 for(var i = 0, n; n = c[i]; i++){
5611                     var pn = n.parentNode;
5612                     if (batch != pn._batch) {
5613                         var j = 0;
5614                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5615                             if(cn.nodeType == 1){
5616                                cn.nodeIndex = ++j;
5617                             }
5618                         }
5619                         pn._batch = batch;
5620                     }
5621                     if (f == 1) {
5622                         if (l == 0 || n.nodeIndex == l){
5623                             r[++ri] = n;
5624                         }
5625                     } else if ((n.nodeIndex + l) % f == 0){
5626                         r[++ri] = n;
5627                     }
5628                 }
5629
5630                 return r;
5631             },
5632
5633             "only-child" : function(c){
5634                 var r = [], ri = -1;;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     if(!prev(ci) && !next(ci)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             },
5642
5643             "empty" : function(c){
5644                 var r = [], ri = -1;
5645                 for(var i = 0, ci; ci = c[i]; i++){
5646                     var cns = ci.childNodes, j = 0, cn, empty = true;
5647                     while(cn = cns[j]){
5648                         ++j;
5649                         if(cn.nodeType == 1 || cn.nodeType == 3){
5650                             empty = false;
5651                             break;
5652                         }
5653                     }
5654                     if(empty){
5655                         r[++ri] = ci;
5656                     }
5657                 }
5658                 return r;
5659             },
5660
5661             "contains" : function(c, v){
5662                 var r = [], ri = -1;
5663                 for(var i = 0, ci; ci = c[i]; i++){
5664                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5665                         r[++ri] = ci;
5666                     }
5667                 }
5668                 return r;
5669             },
5670
5671             "nodeValue" : function(c, v){
5672                 var r = [], ri = -1;
5673                 for(var i = 0, ci; ci = c[i]; i++){
5674                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5675                         r[++ri] = ci;
5676                     }
5677                 }
5678                 return r;
5679             },
5680
5681             "checked" : function(c){
5682                 var r = [], ri = -1;
5683                 for(var i = 0, ci; ci = c[i]; i++){
5684                     if(ci.checked == true){
5685                         r[++ri] = ci;
5686                     }
5687                 }
5688                 return r;
5689             },
5690
5691             "not" : function(c, ss){
5692                 return Roo.DomQuery.filter(c, ss, true);
5693             },
5694
5695             "odd" : function(c){
5696                 return this["nth-child"](c, "odd");
5697             },
5698
5699             "even" : function(c){
5700                 return this["nth-child"](c, "even");
5701             },
5702
5703             "nth" : function(c, a){
5704                 return c[a-1] || [];
5705             },
5706
5707             "first" : function(c){
5708                 return c[0] || [];
5709             },
5710
5711             "last" : function(c){
5712                 return c[c.length-1] || [];
5713             },
5714
5715             "has" : function(c, ss){
5716                 var s = Roo.DomQuery.select;
5717                 var r = [], ri = -1;
5718                 for(var i = 0, ci; ci = c[i]; i++){
5719                     if(s(ss, ci).length > 0){
5720                         r[++ri] = ci;
5721                     }
5722                 }
5723                 return r;
5724             },
5725
5726             "next" : function(c, ss){
5727                 var is = Roo.DomQuery.is;
5728                 var r = [], ri = -1;
5729                 for(var i = 0, ci; ci = c[i]; i++){
5730                     var n = next(ci);
5731                     if(n && is(n, ss)){
5732                         r[++ri] = ci;
5733                     }
5734                 }
5735                 return r;
5736             },
5737
5738             "prev" : function(c, ss){
5739                 var is = Roo.DomQuery.is;
5740                 var r = [], ri = -1;
5741                 for(var i = 0, ci; ci = c[i]; i++){
5742                     var n = prev(ci);
5743                     if(n && is(n, ss)){
5744                         r[++ri] = ci;
5745                     }
5746                 }
5747                 return r;
5748             }
5749         }
5750     };
5751 }();
5752
5753 /**
5754  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5755  * @param {String} path The selector/xpath query
5756  * @param {Node} root (optional) The start of the query (defaults to document).
5757  * @return {Array}
5758  * @member Roo
5759  * @method query
5760  */
5761 Roo.query = Roo.DomQuery.select;
5762 /*
5763  * Based on:
5764  * Ext JS Library 1.1.1
5765  * Copyright(c) 2006-2007, Ext JS, LLC.
5766  *
5767  * Originally Released Under LGPL - original licence link has changed is not relivant.
5768  *
5769  * Fork - LGPL
5770  * <script type="text/javascript">
5771  */
5772
5773 /**
5774  * @class Roo.util.Observable
5775  * Base class that provides a common interface for publishing events. Subclasses are expected to
5776  * to have a property "events" with all the events defined.<br>
5777  * For example:
5778  * <pre><code>
5779  Employee = function(name){
5780     this.name = name;
5781     this.addEvents({
5782         "fired" : true,
5783         "quit" : true
5784     });
5785  }
5786  Roo.extend(Employee, Roo.util.Observable);
5787 </code></pre>
5788  * @param {Object} config properties to use (incuding events / listeners)
5789  */
5790
5791 Roo.util.Observable = function(cfg){
5792     
5793     cfg = cfg|| {};
5794     this.addEvents(cfg.events || {});
5795     if (cfg.events) {
5796         delete cfg.events; // make sure
5797     }
5798      
5799     Roo.apply(this, cfg);
5800     
5801     if(this.listeners){
5802         this.on(this.listeners);
5803         delete this.listeners;
5804     }
5805 };
5806 Roo.util.Observable.prototype = {
5807     /** 
5808  * @cfg {Object} listeners  list of events and functions to call for this object, 
5809  * For example :
5810  * <pre><code>
5811     listeners :  { 
5812        'click' : function(e) {
5813            ..... 
5814         } ,
5815         .... 
5816     } 
5817   </code></pre>
5818  */
5819     
5820     
5821     /**
5822      * Fires the specified event with the passed parameters (minus the event name).
5823      * @param {String} eventName
5824      * @param {Object...} args Variable number of parameters are passed to handlers
5825      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5826      */
5827     fireEvent : function(){
5828         var ce = this.events[arguments[0].toLowerCase()];
5829         if(typeof ce == "object"){
5830             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5831         }else{
5832             return true;
5833         }
5834     },
5835
5836     // private
5837     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5838
5839     /**
5840      * Appends an event handler to this component
5841      * @param {String}   eventName The type of event to listen for
5842      * @param {Function} handler The method the event invokes
5843      * @param {Object}   scope (optional) The scope in which to execute the handler
5844      * function. The handler function's "this" context.
5845      * @param {Object}   options (optional) An object containing handler configuration
5846      * properties. This may contain any of the following properties:<ul>
5847      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5848      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5849      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5850      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5851      * by the specified number of milliseconds. If the event fires again within that time, the original
5852      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5853      * </ul><br>
5854      * <p>
5855      * <b>Combining Options</b><br>
5856      * Using the options argument, it is possible to combine different types of listeners:<br>
5857      * <br>
5858      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5859                 <pre><code>
5860                 el.on('click', this.onClick, this, {
5861                         single: true,
5862                 delay: 100,
5863                 forumId: 4
5864                 });
5865                 </code></pre>
5866      * <p>
5867      * <b>Attaching multiple handlers in 1 call</b><br>
5868      * The method also allows for a single argument to be passed which is a config object containing properties
5869      * which specify multiple handlers.
5870      * <pre><code>
5871                 el.on({
5872                         'click': {
5873                         fn: this.onClick,
5874                         scope: this,
5875                         delay: 100
5876                 }, 
5877                 'mouseover': {
5878                         fn: this.onMouseOver,
5879                         scope: this
5880                 },
5881                 'mouseout': {
5882                         fn: this.onMouseOut,
5883                         scope: this
5884                 }
5885                 });
5886                 </code></pre>
5887      * <p>
5888      * Or a shorthand syntax which passes the same scope object to all handlers:
5889         <pre><code>
5890                 el.on({
5891                         'click': this.onClick,
5892                 'mouseover': this.onMouseOver,
5893                 'mouseout': this.onMouseOut,
5894                 scope: this
5895                 });
5896                 </code></pre>
5897      */
5898     addListener : function(eventName, fn, scope, o){
5899         if(typeof eventName == "object"){
5900             o = eventName;
5901             for(var e in o){
5902                 if(this.filterOptRe.test(e)){
5903                     continue;
5904                 }
5905                 if(typeof o[e] == "function"){
5906                     // shared options
5907                     this.addListener(e, o[e], o.scope,  o);
5908                 }else{
5909                     // individual options
5910                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5911                 }
5912             }
5913             return;
5914         }
5915         o = (!o || typeof o == "boolean") ? {} : o;
5916         eventName = eventName.toLowerCase();
5917         var ce = this.events[eventName] || true;
5918         if(typeof ce == "boolean"){
5919             ce = new Roo.util.Event(this, eventName);
5920             this.events[eventName] = ce;
5921         }
5922         ce.addListener(fn, scope, o);
5923     },
5924
5925     /**
5926      * Removes a listener
5927      * @param {String}   eventName     The type of event to listen for
5928      * @param {Function} handler        The handler to remove
5929      * @param {Object}   scope  (optional) The scope (this object) for the handler
5930      */
5931     removeListener : function(eventName, fn, scope){
5932         var ce = this.events[eventName.toLowerCase()];
5933         if(typeof ce == "object"){
5934             ce.removeListener(fn, scope);
5935         }
5936     },
5937
5938     /**
5939      * Removes all listeners for this object
5940      */
5941     purgeListeners : function(){
5942         for(var evt in this.events){
5943             if(typeof this.events[evt] == "object"){
5944                  this.events[evt].clearListeners();
5945             }
5946         }
5947     },
5948
5949     relayEvents : function(o, events){
5950         var createHandler = function(ename){
5951             return function(){
5952                  
5953                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5954             };
5955         };
5956         for(var i = 0, len = events.length; i < len; i++){
5957             var ename = events[i];
5958             if(!this.events[ename]){
5959                 this.events[ename] = true;
5960             };
5961             o.on(ename, createHandler(ename), this);
5962         }
5963     },
5964
5965     /**
5966      * Used to define events on this Observable
5967      * @param {Object} object The object with the events defined
5968      */
5969     addEvents : function(o){
5970         if(!this.events){
5971             this.events = {};
5972         }
5973         Roo.applyIf(this.events, o);
5974     },
5975
5976     /**
5977      * Checks to see if this object has any listeners for a specified event
5978      * @param {String} eventName The name of the event to check for
5979      * @return {Boolean} True if the event is being listened for, else false
5980      */
5981     hasListener : function(eventName){
5982         var e = this.events[eventName];
5983         return typeof e == "object" && e.listeners.length > 0;
5984     }
5985 };
5986 /**
5987  * Appends an event handler to this element (shorthand for addListener)
5988  * @param {String}   eventName     The type of event to listen for
5989  * @param {Function} handler        The method the event invokes
5990  * @param {Object}   scope (optional) The scope in which to execute the handler
5991  * function. The handler function's "this" context.
5992  * @param {Object}   options  (optional)
5993  * @method
5994  */
5995 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5996 /**
5997  * Removes a listener (shorthand for removeListener)
5998  * @param {String}   eventName     The type of event to listen for
5999  * @param {Function} handler        The handler to remove
6000  * @param {Object}   scope  (optional) The scope (this object) for the handler
6001  * @method
6002  */
6003 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6004
6005 /**
6006  * Starts capture on the specified Observable. All events will be passed
6007  * to the supplied function with the event name + standard signature of the event
6008  * <b>before</b> the event is fired. If the supplied function returns false,
6009  * the event will not fire.
6010  * @param {Observable} o The Observable to capture
6011  * @param {Function} fn The function to call
6012  * @param {Object} scope (optional) The scope (this object) for the fn
6013  * @static
6014  */
6015 Roo.util.Observable.capture = function(o, fn, scope){
6016     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6017 };
6018
6019 /**
6020  * Removes <b>all</b> added captures from the Observable.
6021  * @param {Observable} o The Observable to release
6022  * @static
6023  */
6024 Roo.util.Observable.releaseCapture = function(o){
6025     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6026 };
6027
6028 (function(){
6029
6030     var createBuffered = function(h, o, scope){
6031         var task = new Roo.util.DelayedTask();
6032         return function(){
6033             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6034         };
6035     };
6036
6037     var createSingle = function(h, e, fn, scope){
6038         return function(){
6039             e.removeListener(fn, scope);
6040             return h.apply(scope, arguments);
6041         };
6042     };
6043
6044     var createDelayed = function(h, o, scope){
6045         return function(){
6046             var args = Array.prototype.slice.call(arguments, 0);
6047             setTimeout(function(){
6048                 h.apply(scope, args);
6049             }, o.delay || 10);
6050         };
6051     };
6052
6053     Roo.util.Event = function(obj, name){
6054         this.name = name;
6055         this.obj = obj;
6056         this.listeners = [];
6057     };
6058
6059     Roo.util.Event.prototype = {
6060         addListener : function(fn, scope, options){
6061             var o = options || {};
6062             scope = scope || this.obj;
6063             if(!this.isListening(fn, scope)){
6064                 var l = {fn: fn, scope: scope, options: o};
6065                 var h = fn;
6066                 if(o.delay){
6067                     h = createDelayed(h, o, scope);
6068                 }
6069                 if(o.single){
6070                     h = createSingle(h, this, fn, scope);
6071                 }
6072                 if(o.buffer){
6073                     h = createBuffered(h, o, scope);
6074                 }
6075                 l.fireFn = h;
6076                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6077                     this.listeners.push(l);
6078                 }else{
6079                     this.listeners = this.listeners.slice(0);
6080                     this.listeners.push(l);
6081                 }
6082             }
6083         },
6084
6085         findListener : function(fn, scope){
6086             scope = scope || this.obj;
6087             var ls = this.listeners;
6088             for(var i = 0, len = ls.length; i < len; i++){
6089                 var l = ls[i];
6090                 if(l.fn == fn && l.scope == scope){
6091                     return i;
6092                 }
6093             }
6094             return -1;
6095         },
6096
6097         isListening : function(fn, scope){
6098             return this.findListener(fn, scope) != -1;
6099         },
6100
6101         removeListener : function(fn, scope){
6102             var index;
6103             if((index = this.findListener(fn, scope)) != -1){
6104                 if(!this.firing){
6105                     this.listeners.splice(index, 1);
6106                 }else{
6107                     this.listeners = this.listeners.slice(0);
6108                     this.listeners.splice(index, 1);
6109                 }
6110                 return true;
6111             }
6112             return false;
6113         },
6114
6115         clearListeners : function(){
6116             this.listeners = [];
6117         },
6118
6119         fire : function(){
6120             var ls = this.listeners, scope, len = ls.length;
6121             if(len > 0){
6122                 this.firing = true;
6123                 var args = Array.prototype.slice.call(arguments, 0);                
6124                 for(var i = 0; i < len; i++){
6125                     var l = ls[i];
6126                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6127                         this.firing = false;
6128                         return false;
6129                     }
6130                 }
6131                 this.firing = false;
6132             }
6133             return true;
6134         }
6135     };
6136 })();/*
6137  * RooJS Library 
6138  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6139  *
6140  * Licence LGPL 
6141  *
6142  */
6143  
6144 /**
6145  * @class Roo.Document
6146  * @extends Roo.util.Observable
6147  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6148  * 
6149  * @param {Object} config the methods and properties of the 'base' class for the application.
6150  * 
6151  *  Generic Page handler - implement this to start your app..
6152  * 
6153  * eg.
6154  *  MyProject = new Roo.Document({
6155         events : {
6156             'load' : true // your events..
6157         },
6158         listeners : {
6159             'ready' : function() {
6160                 // fired on Roo.onReady()
6161             }
6162         }
6163  * 
6164  */
6165 Roo.Document = function(cfg) {
6166      
6167     this.addEvents({ 
6168         'ready' : true
6169     });
6170     Roo.util.Observable.call(this,cfg);
6171     
6172     var _this = this;
6173     
6174     Roo.onReady(function() {
6175         _this.fireEvent('ready');
6176     },null,false);
6177     
6178     
6179 }
6180
6181 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6182  * Based on:
6183  * Ext JS Library 1.1.1
6184  * Copyright(c) 2006-2007, Ext JS, LLC.
6185  *
6186  * Originally Released Under LGPL - original licence link has changed is not relivant.
6187  *
6188  * Fork - LGPL
6189  * <script type="text/javascript">
6190  */
6191
6192 /**
6193  * @class Roo.EventManager
6194  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6195  * several useful events directly.
6196  * See {@link Roo.EventObject} for more details on normalized event objects.
6197  * @singleton
6198  */
6199 Roo.EventManager = function(){
6200     var docReadyEvent, docReadyProcId, docReadyState = false;
6201     var resizeEvent, resizeTask, textEvent, textSize;
6202     var E = Roo.lib.Event;
6203     var D = Roo.lib.Dom;
6204
6205     
6206     
6207
6208     var fireDocReady = function(){
6209         if(!docReadyState){
6210             docReadyState = true;
6211             Roo.isReady = true;
6212             if(docReadyProcId){
6213                 clearInterval(docReadyProcId);
6214             }
6215             if(Roo.isGecko || Roo.isOpera) {
6216                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6217             }
6218             if(Roo.isIE){
6219                 var defer = document.getElementById("ie-deferred-loader");
6220                 if(defer){
6221                     defer.onreadystatechange = null;
6222                     defer.parentNode.removeChild(defer);
6223                 }
6224             }
6225             if(docReadyEvent){
6226                 docReadyEvent.fire();
6227                 docReadyEvent.clearListeners();
6228             }
6229         }
6230     };
6231     
6232     var initDocReady = function(){
6233         docReadyEvent = new Roo.util.Event();
6234         if(Roo.isGecko || Roo.isOpera) {
6235             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6236         }else if(Roo.isIE){
6237             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6238             var defer = document.getElementById("ie-deferred-loader");
6239             defer.onreadystatechange = function(){
6240                 if(this.readyState == "complete"){
6241                     fireDocReady();
6242                 }
6243             };
6244         }else if(Roo.isSafari){ 
6245             docReadyProcId = setInterval(function(){
6246                 var rs = document.readyState;
6247                 if(rs == "complete") {
6248                     fireDocReady();     
6249                  }
6250             }, 10);
6251         }
6252         // no matter what, make sure it fires on load
6253         E.on(window, "load", fireDocReady);
6254     };
6255
6256     var createBuffered = function(h, o){
6257         var task = new Roo.util.DelayedTask(h);
6258         return function(e){
6259             // create new event object impl so new events don't wipe out properties
6260             e = new Roo.EventObjectImpl(e);
6261             task.delay(o.buffer, h, null, [e]);
6262         };
6263     };
6264
6265     var createSingle = function(h, el, ename, fn){
6266         return function(e){
6267             Roo.EventManager.removeListener(el, ename, fn);
6268             h(e);
6269         };
6270     };
6271
6272     var createDelayed = function(h, o){
6273         return function(e){
6274             // create new event object impl so new events don't wipe out properties
6275             e = new Roo.EventObjectImpl(e);
6276             setTimeout(function(){
6277                 h(e);
6278             }, o.delay || 10);
6279         };
6280     };
6281     var transitionEndVal = false;
6282     
6283     var transitionEnd = function()
6284     {
6285         if (transitionEndVal) {
6286             return transitionEndVal;
6287         }
6288         var el = document.createElement('div');
6289
6290         var transEndEventNames = {
6291             WebkitTransition : 'webkitTransitionEnd',
6292             MozTransition    : 'transitionend',
6293             OTransition      : 'oTransitionEnd otransitionend',
6294             transition       : 'transitionend'
6295         };
6296     
6297         for (var name in transEndEventNames) {
6298             if (el.style[name] !== undefined) {
6299                 transitionEndVal = transEndEventNames[name];
6300                 return  transitionEndVal ;
6301             }
6302         }
6303     }
6304     
6305   
6306
6307     var listen = function(element, ename, opt, fn, scope){
6308         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6309         fn = fn || o.fn; scope = scope || o.scope;
6310         var el = Roo.getDom(element);
6311         
6312         
6313         if(!el){
6314             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6315         }
6316         
6317         if (ename == 'transitionend') {
6318             ename = transitionEnd();
6319         }
6320         var h = function(e){
6321             e = Roo.EventObject.setEvent(e);
6322             var t;
6323             if(o.delegate){
6324                 t = e.getTarget(o.delegate, el);
6325                 if(!t){
6326                     return;
6327                 }
6328             }else{
6329                 t = e.target;
6330             }
6331             if(o.stopEvent === true){
6332                 e.stopEvent();
6333             }
6334             if(o.preventDefault === true){
6335                e.preventDefault();
6336             }
6337             if(o.stopPropagation === true){
6338                 e.stopPropagation();
6339             }
6340
6341             if(o.normalized === false){
6342                 e = e.browserEvent;
6343             }
6344
6345             fn.call(scope || el, e, t, o);
6346         };
6347         if(o.delay){
6348             h = createDelayed(h, o);
6349         }
6350         if(o.single){
6351             h = createSingle(h, el, ename, fn);
6352         }
6353         if(o.buffer){
6354             h = createBuffered(h, o);
6355         }
6356         
6357         fn._handlers = fn._handlers || [];
6358         
6359         
6360         fn._handlers.push([Roo.id(el), ename, h]);
6361         
6362         
6363          
6364         E.on(el, ename, h);
6365         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6366             el.addEventListener("DOMMouseScroll", h, false);
6367             E.on(window, 'unload', function(){
6368                 el.removeEventListener("DOMMouseScroll", h, false);
6369             });
6370         }
6371         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6372             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6373         }
6374         return h;
6375     };
6376
6377     var stopListening = function(el, ename, fn){
6378         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6379         if(hds){
6380             for(var i = 0, len = hds.length; i < len; i++){
6381                 var h = hds[i];
6382                 if(h[0] == id && h[1] == ename){
6383                     hd = h[2];
6384                     hds.splice(i, 1);
6385                     break;
6386                 }
6387             }
6388         }
6389         E.un(el, ename, hd);
6390         el = Roo.getDom(el);
6391         if(ename == "mousewheel" && el.addEventListener){
6392             el.removeEventListener("DOMMouseScroll", hd, false);
6393         }
6394         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6395             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6396         }
6397     };
6398
6399     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6400     
6401     var pub = {
6402         
6403         
6404         /** 
6405          * Fix for doc tools
6406          * @scope Roo.EventManager
6407          */
6408         
6409         
6410         /** 
6411          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6412          * object with a Roo.EventObject
6413          * @param {Function} fn        The method the event invokes
6414          * @param {Object}   scope    An object that becomes the scope of the handler
6415          * @param {boolean}  override If true, the obj passed in becomes
6416          *                             the execution scope of the listener
6417          * @return {Function} The wrapped function
6418          * @deprecated
6419          */
6420         wrap : function(fn, scope, override){
6421             return function(e){
6422                 Roo.EventObject.setEvent(e);
6423                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6424             };
6425         },
6426         
6427         /**
6428      * Appends an event handler to an element (shorthand for addListener)
6429      * @param {String/HTMLElement}   element        The html element or id to assign the
6430      * @param {String}   eventName The type of event to listen for
6431      * @param {Function} handler The method the event invokes
6432      * @param {Object}   scope (optional) The scope in which to execute the handler
6433      * function. The handler function's "this" context.
6434      * @param {Object}   options (optional) An object containing handler configuration
6435      * properties. This may contain any of the following properties:<ul>
6436      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6437      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6438      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6439      * <li>preventDefault {Boolean} True to prevent the default action</li>
6440      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6441      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6442      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6443      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6444      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6445      * by the specified number of milliseconds. If the event fires again within that time, the original
6446      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6447      * </ul><br>
6448      * <p>
6449      * <b>Combining Options</b><br>
6450      * Using the options argument, it is possible to combine different types of listeners:<br>
6451      * <br>
6452      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6453      * Code:<pre><code>
6454 el.on('click', this.onClick, this, {
6455     single: true,
6456     delay: 100,
6457     stopEvent : true,
6458     forumId: 4
6459 });</code></pre>
6460      * <p>
6461      * <b>Attaching multiple handlers in 1 call</b><br>
6462       * The method also allows for a single argument to be passed which is a config object containing properties
6463      * which specify multiple handlers.
6464      * <p>
6465      * Code:<pre><code>
6466 el.on({
6467     'click' : {
6468         fn: this.onClick
6469         scope: this,
6470         delay: 100
6471     },
6472     'mouseover' : {
6473         fn: this.onMouseOver
6474         scope: this
6475     },
6476     'mouseout' : {
6477         fn: this.onMouseOut
6478         scope: this
6479     }
6480 });</code></pre>
6481      * <p>
6482      * Or a shorthand syntax:<br>
6483      * Code:<pre><code>
6484 el.on({
6485     'click' : this.onClick,
6486     'mouseover' : this.onMouseOver,
6487     'mouseout' : this.onMouseOut
6488     scope: this
6489 });</code></pre>
6490      */
6491         addListener : function(element, eventName, fn, scope, options){
6492             if(typeof eventName == "object"){
6493                 var o = eventName;
6494                 for(var e in o){
6495                     if(propRe.test(e)){
6496                         continue;
6497                     }
6498                     if(typeof o[e] == "function"){
6499                         // shared options
6500                         listen(element, e, o, o[e], o.scope);
6501                     }else{
6502                         // individual options
6503                         listen(element, e, o[e]);
6504                     }
6505                 }
6506                 return;
6507             }
6508             return listen(element, eventName, options, fn, scope);
6509         },
6510         
6511         /**
6512          * Removes an event handler
6513          *
6514          * @param {String/HTMLElement}   element        The id or html element to remove the 
6515          *                             event from
6516          * @param {String}   eventName     The type of event
6517          * @param {Function} fn
6518          * @return {Boolean} True if a listener was actually removed
6519          */
6520         removeListener : function(element, eventName, fn){
6521             return stopListening(element, eventName, fn);
6522         },
6523         
6524         /**
6525          * Fires when the document is ready (before onload and before images are loaded). Can be 
6526          * accessed shorthanded Roo.onReady().
6527          * @param {Function} fn        The method the event invokes
6528          * @param {Object}   scope    An  object that becomes the scope of the handler
6529          * @param {boolean}  options
6530          */
6531         onDocumentReady : function(fn, scope, options){
6532             if(docReadyState){ // if it already fired
6533                 docReadyEvent.addListener(fn, scope, options);
6534                 docReadyEvent.fire();
6535                 docReadyEvent.clearListeners();
6536                 return;
6537             }
6538             if(!docReadyEvent){
6539                 initDocReady();
6540             }
6541             docReadyEvent.addListener(fn, scope, options);
6542         },
6543         
6544         /**
6545          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6546          * @param {Function} fn        The method the event invokes
6547          * @param {Object}   scope    An object that becomes the scope of the handler
6548          * @param {boolean}  options
6549          */
6550         onWindowResize : function(fn, scope, options){
6551             if(!resizeEvent){
6552                 resizeEvent = new Roo.util.Event();
6553                 resizeTask = new Roo.util.DelayedTask(function(){
6554                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6555                 });
6556                 E.on(window, "resize", function(){
6557                     if(Roo.isIE){
6558                         resizeTask.delay(50);
6559                     }else{
6560                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6561                     }
6562                 });
6563             }
6564             resizeEvent.addListener(fn, scope, options);
6565         },
6566
6567         /**
6568          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6569          * @param {Function} fn        The method the event invokes
6570          * @param {Object}   scope    An object that becomes the scope of the handler
6571          * @param {boolean}  options
6572          */
6573         onTextResize : function(fn, scope, options){
6574             if(!textEvent){
6575                 textEvent = new Roo.util.Event();
6576                 var textEl = new Roo.Element(document.createElement('div'));
6577                 textEl.dom.className = 'x-text-resize';
6578                 textEl.dom.innerHTML = 'X';
6579                 textEl.appendTo(document.body);
6580                 textSize = textEl.dom.offsetHeight;
6581                 setInterval(function(){
6582                     if(textEl.dom.offsetHeight != textSize){
6583                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6584                     }
6585                 }, this.textResizeInterval);
6586             }
6587             textEvent.addListener(fn, scope, options);
6588         },
6589
6590         /**
6591          * Removes the passed window resize listener.
6592          * @param {Function} fn        The method the event invokes
6593          * @param {Object}   scope    The scope of handler
6594          */
6595         removeResizeListener : function(fn, scope){
6596             if(resizeEvent){
6597                 resizeEvent.removeListener(fn, scope);
6598             }
6599         },
6600
6601         // private
6602         fireResize : function(){
6603             if(resizeEvent){
6604                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6605             }   
6606         },
6607         /**
6608          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6609          */
6610         ieDeferSrc : false,
6611         /**
6612          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6613          */
6614         textResizeInterval : 50
6615     };
6616     
6617     /**
6618      * Fix for doc tools
6619      * @scopeAlias pub=Roo.EventManager
6620      */
6621     
6622      /**
6623      * Appends an event handler to an element (shorthand for addListener)
6624      * @param {String/HTMLElement}   element        The html element or id to assign the
6625      * @param {String}   eventName The type of event to listen for
6626      * @param {Function} handler The method the event invokes
6627      * @param {Object}   scope (optional) The scope in which to execute the handler
6628      * function. The handler function's "this" context.
6629      * @param {Object}   options (optional) An object containing handler configuration
6630      * properties. This may contain any of the following properties:<ul>
6631      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6632      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6633      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6634      * <li>preventDefault {Boolean} True to prevent the default action</li>
6635      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6636      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6637      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6638      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6639      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6640      * by the specified number of milliseconds. If the event fires again within that time, the original
6641      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6642      * </ul><br>
6643      * <p>
6644      * <b>Combining Options</b><br>
6645      * Using the options argument, it is possible to combine different types of listeners:<br>
6646      * <br>
6647      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6648      * Code:<pre><code>
6649 el.on('click', this.onClick, this, {
6650     single: true,
6651     delay: 100,
6652     stopEvent : true,
6653     forumId: 4
6654 });</code></pre>
6655      * <p>
6656      * <b>Attaching multiple handlers in 1 call</b><br>
6657       * The method also allows for a single argument to be passed which is a config object containing properties
6658      * which specify multiple handlers.
6659      * <p>
6660      * Code:<pre><code>
6661 el.on({
6662     'click' : {
6663         fn: this.onClick
6664         scope: this,
6665         delay: 100
6666     },
6667     'mouseover' : {
6668         fn: this.onMouseOver
6669         scope: this
6670     },
6671     'mouseout' : {
6672         fn: this.onMouseOut
6673         scope: this
6674     }
6675 });</code></pre>
6676      * <p>
6677      * Or a shorthand syntax:<br>
6678      * Code:<pre><code>
6679 el.on({
6680     'click' : this.onClick,
6681     'mouseover' : this.onMouseOver,
6682     'mouseout' : this.onMouseOut
6683     scope: this
6684 });</code></pre>
6685      */
6686     pub.on = pub.addListener;
6687     pub.un = pub.removeListener;
6688
6689     pub.stoppedMouseDownEvent = new Roo.util.Event();
6690     return pub;
6691 }();
6692 /**
6693   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6694   * @param {Function} fn        The method the event invokes
6695   * @param {Object}   scope    An  object that becomes the scope of the handler
6696   * @param {boolean}  override If true, the obj passed in becomes
6697   *                             the execution scope of the listener
6698   * @member Roo
6699   * @method onReady
6700  */
6701 Roo.onReady = Roo.EventManager.onDocumentReady;
6702
6703 Roo.onReady(function(){
6704     var bd = Roo.get(document.body);
6705     if(!bd){ return; }
6706
6707     var cls = [
6708             Roo.isIE ? "roo-ie"
6709             : Roo.isIE11 ? "roo-ie11"
6710             : Roo.isEdge ? "roo-edge"
6711             : Roo.isGecko ? "roo-gecko"
6712             : Roo.isOpera ? "roo-opera"
6713             : Roo.isSafari ? "roo-safari" : ""];
6714
6715     if(Roo.isMac){
6716         cls.push("roo-mac");
6717     }
6718     if(Roo.isLinux){
6719         cls.push("roo-linux");
6720     }
6721     if(Roo.isIOS){
6722         cls.push("roo-ios");
6723     }
6724     if(Roo.isTouch){
6725         cls.push("roo-touch");
6726     }
6727     if(Roo.isBorderBox){
6728         cls.push('roo-border-box');
6729     }
6730     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6731         var p = bd.dom.parentNode;
6732         if(p){
6733             p.className += ' roo-strict';
6734         }
6735     }
6736     bd.addClass(cls.join(' '));
6737 });
6738
6739 /**
6740  * @class Roo.EventObject
6741  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6742  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6743  * Example:
6744  * <pre><code>
6745  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6746     e.preventDefault();
6747     var target = e.getTarget();
6748     ...
6749  }
6750  var myDiv = Roo.get("myDiv");
6751  myDiv.on("click", handleClick);
6752  //or
6753  Roo.EventManager.on("myDiv", 'click', handleClick);
6754  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6755  </code></pre>
6756  * @singleton
6757  */
6758 Roo.EventObject = function(){
6759     
6760     var E = Roo.lib.Event;
6761     
6762     // safari keypress events for special keys return bad keycodes
6763     var safariKeys = {
6764         63234 : 37, // left
6765         63235 : 39, // right
6766         63232 : 38, // up
6767         63233 : 40, // down
6768         63276 : 33, // page up
6769         63277 : 34, // page down
6770         63272 : 46, // delete
6771         63273 : 36, // home
6772         63275 : 35  // end
6773     };
6774
6775     // normalize button clicks
6776     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6777                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6778
6779     Roo.EventObjectImpl = function(e){
6780         if(e){
6781             this.setEvent(e.browserEvent || e);
6782         }
6783     };
6784     Roo.EventObjectImpl.prototype = {
6785         /**
6786          * Used to fix doc tools.
6787          * @scope Roo.EventObject.prototype
6788          */
6789             
6790
6791         
6792         
6793         /** The normal browser event */
6794         browserEvent : null,
6795         /** The button pressed in a mouse event */
6796         button : -1,
6797         /** True if the shift key was down during the event */
6798         shiftKey : false,
6799         /** True if the control key was down during the event */
6800         ctrlKey : false,
6801         /** True if the alt key was down during the event */
6802         altKey : false,
6803
6804         /** Key constant 
6805         * @type Number */
6806         BACKSPACE : 8,
6807         /** Key constant 
6808         * @type Number */
6809         TAB : 9,
6810         /** Key constant 
6811         * @type Number */
6812         RETURN : 13,
6813         /** Key constant 
6814         * @type Number */
6815         ENTER : 13,
6816         /** Key constant 
6817         * @type Number */
6818         SHIFT : 16,
6819         /** Key constant 
6820         * @type Number */
6821         CONTROL : 17,
6822         /** Key constant 
6823         * @type Number */
6824         ESC : 27,
6825         /** Key constant 
6826         * @type Number */
6827         SPACE : 32,
6828         /** Key constant 
6829         * @type Number */
6830         PAGEUP : 33,
6831         /** Key constant 
6832         * @type Number */
6833         PAGEDOWN : 34,
6834         /** Key constant 
6835         * @type Number */
6836         END : 35,
6837         /** Key constant 
6838         * @type Number */
6839         HOME : 36,
6840         /** Key constant 
6841         * @type Number */
6842         LEFT : 37,
6843         /** Key constant 
6844         * @type Number */
6845         UP : 38,
6846         /** Key constant 
6847         * @type Number */
6848         RIGHT : 39,
6849         /** Key constant 
6850         * @type Number */
6851         DOWN : 40,
6852         /** Key constant 
6853         * @type Number */
6854         DELETE : 46,
6855         /** Key constant 
6856         * @type Number */
6857         F5 : 116,
6858
6859            /** @private */
6860         setEvent : function(e){
6861             if(e == this || (e && e.browserEvent)){ // already wrapped
6862                 return e;
6863             }
6864             this.browserEvent = e;
6865             if(e){
6866                 // normalize buttons
6867                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6868                 if(e.type == 'click' && this.button == -1){
6869                     this.button = 0;
6870                 }
6871                 this.type = e.type;
6872                 this.shiftKey = e.shiftKey;
6873                 // mac metaKey behaves like ctrlKey
6874                 this.ctrlKey = e.ctrlKey || e.metaKey;
6875                 this.altKey = e.altKey;
6876                 // in getKey these will be normalized for the mac
6877                 this.keyCode = e.keyCode;
6878                 // keyup warnings on firefox.
6879                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6880                 // cache the target for the delayed and or buffered events
6881                 this.target = E.getTarget(e);
6882                 // same for XY
6883                 this.xy = E.getXY(e);
6884             }else{
6885                 this.button = -1;
6886                 this.shiftKey = false;
6887                 this.ctrlKey = false;
6888                 this.altKey = false;
6889                 this.keyCode = 0;
6890                 this.charCode =0;
6891                 this.target = null;
6892                 this.xy = [0, 0];
6893             }
6894             return this;
6895         },
6896
6897         /**
6898          * Stop the event (preventDefault and stopPropagation)
6899          */
6900         stopEvent : function(){
6901             if(this.browserEvent){
6902                 if(this.browserEvent.type == 'mousedown'){
6903                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6904                 }
6905                 E.stopEvent(this.browserEvent);
6906             }
6907         },
6908
6909         /**
6910          * Prevents the browsers default handling of the event.
6911          */
6912         preventDefault : function(){
6913             if(this.browserEvent){
6914                 E.preventDefault(this.browserEvent);
6915             }
6916         },
6917
6918         /** @private */
6919         isNavKeyPress : function(){
6920             var k = this.keyCode;
6921             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6922             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6923         },
6924
6925         isSpecialKey : function(){
6926             var k = this.keyCode;
6927             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6928             (k == 16) || (k == 17) ||
6929             (k >= 18 && k <= 20) ||
6930             (k >= 33 && k <= 35) ||
6931             (k >= 36 && k <= 39) ||
6932             (k >= 44 && k <= 45);
6933         },
6934         /**
6935          * Cancels bubbling of the event.
6936          */
6937         stopPropagation : function(){
6938             if(this.browserEvent){
6939                 if(this.type == 'mousedown'){
6940                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6941                 }
6942                 E.stopPropagation(this.browserEvent);
6943             }
6944         },
6945
6946         /**
6947          * Gets the key code for the event.
6948          * @return {Number}
6949          */
6950         getCharCode : function(){
6951             return this.charCode || this.keyCode;
6952         },
6953
6954         /**
6955          * Returns a normalized keyCode for the event.
6956          * @return {Number} The key code
6957          */
6958         getKey : function(){
6959             var k = this.keyCode || this.charCode;
6960             return Roo.isSafari ? (safariKeys[k] || k) : k;
6961         },
6962
6963         /**
6964          * Gets the x coordinate of the event.
6965          * @return {Number}
6966          */
6967         getPageX : function(){
6968             return this.xy[0];
6969         },
6970
6971         /**
6972          * Gets the y coordinate of the event.
6973          * @return {Number}
6974          */
6975         getPageY : function(){
6976             return this.xy[1];
6977         },
6978
6979         /**
6980          * Gets the time of the event.
6981          * @return {Number}
6982          */
6983         getTime : function(){
6984             if(this.browserEvent){
6985                 return E.getTime(this.browserEvent);
6986             }
6987             return null;
6988         },
6989
6990         /**
6991          * Gets the page coordinates of the event.
6992          * @return {Array} The xy values like [x, y]
6993          */
6994         getXY : function(){
6995             return this.xy;
6996         },
6997
6998         /**
6999          * Gets the target for the event.
7000          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7001          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7002                 search as a number or element (defaults to 10 || document.body)
7003          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7004          * @return {HTMLelement}
7005          */
7006         getTarget : function(selector, maxDepth, returnEl){
7007             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7008         },
7009         /**
7010          * Gets the related target.
7011          * @return {HTMLElement}
7012          */
7013         getRelatedTarget : function(){
7014             if(this.browserEvent){
7015                 return E.getRelatedTarget(this.browserEvent);
7016             }
7017             return null;
7018         },
7019
7020         /**
7021          * Normalizes mouse wheel delta across browsers
7022          * @return {Number} The delta
7023          */
7024         getWheelDelta : function(){
7025             var e = this.browserEvent;
7026             var delta = 0;
7027             if(e.wheelDelta){ /* IE/Opera. */
7028                 delta = e.wheelDelta/120;
7029             }else if(e.detail){ /* Mozilla case. */
7030                 delta = -e.detail/3;
7031             }
7032             return delta;
7033         },
7034
7035         /**
7036          * Returns true if the control, meta, shift or alt key was pressed during this event.
7037          * @return {Boolean}
7038          */
7039         hasModifier : function(){
7040             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7041         },
7042
7043         /**
7044          * Returns true if the target of this event equals el or is a child of el
7045          * @param {String/HTMLElement/Element} el
7046          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7047          * @return {Boolean}
7048          */
7049         within : function(el, related){
7050             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7051             return t && Roo.fly(el).contains(t);
7052         },
7053
7054         getPoint : function(){
7055             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7056         }
7057     };
7058
7059     return new Roo.EventObjectImpl();
7060 }();
7061             
7062     /*
7063  * Based on:
7064  * Ext JS Library 1.1.1
7065  * Copyright(c) 2006-2007, Ext JS, LLC.
7066  *
7067  * Originally Released Under LGPL - original licence link has changed is not relivant.
7068  *
7069  * Fork - LGPL
7070  * <script type="text/javascript">
7071  */
7072
7073  
7074 // was in Composite Element!??!?!
7075  
7076 (function(){
7077     var D = Roo.lib.Dom;
7078     var E = Roo.lib.Event;
7079     var A = Roo.lib.Anim;
7080
7081     // local style camelizing for speed
7082     var propCache = {};
7083     var camelRe = /(-[a-z])/gi;
7084     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7085     var view = document.defaultView;
7086
7087 /**
7088  * @class Roo.Element
7089  * Represents an Element in the DOM.<br><br>
7090  * Usage:<br>
7091 <pre><code>
7092 var el = Roo.get("my-div");
7093
7094 // or with getEl
7095 var el = getEl("my-div");
7096
7097 // or with a DOM element
7098 var el = Roo.get(myDivElement);
7099 </code></pre>
7100  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7101  * each call instead of constructing a new one.<br><br>
7102  * <b>Animations</b><br />
7103  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7104  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7105 <pre>
7106 Option    Default   Description
7107 --------- --------  ---------------------------------------------
7108 duration  .35       The duration of the animation in seconds
7109 easing    easeOut   The YUI easing method
7110 callback  none      A function to execute when the anim completes
7111 scope     this      The scope (this) of the callback function
7112 </pre>
7113 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7114 * manipulate the animation. Here's an example:
7115 <pre><code>
7116 var el = Roo.get("my-div");
7117
7118 // no animation
7119 el.setWidth(100);
7120
7121 // default animation
7122 el.setWidth(100, true);
7123
7124 // animation with some options set
7125 el.setWidth(100, {
7126     duration: 1,
7127     callback: this.foo,
7128     scope: this
7129 });
7130
7131 // using the "anim" property to get the Anim object
7132 var opt = {
7133     duration: 1,
7134     callback: this.foo,
7135     scope: this
7136 };
7137 el.setWidth(100, opt);
7138 ...
7139 if(opt.anim.isAnimated()){
7140     opt.anim.stop();
7141 }
7142 </code></pre>
7143 * <b> Composite (Collections of) Elements</b><br />
7144  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7145  * @constructor Create a new Element directly.
7146  * @param {String/HTMLElement} element
7147  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7148  */
7149     Roo.Element = function(element, forceNew){
7150         var dom = typeof element == "string" ?
7151                 document.getElementById(element) : element;
7152         if(!dom){ // invalid id/element
7153             return null;
7154         }
7155         var id = dom.id;
7156         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7157             return Roo.Element.cache[id];
7158         }
7159
7160         /**
7161          * The DOM element
7162          * @type HTMLElement
7163          */
7164         this.dom = dom;
7165
7166         /**
7167          * The DOM element ID
7168          * @type String
7169          */
7170         this.id = id || Roo.id(dom);
7171     };
7172
7173     var El = Roo.Element;
7174
7175     El.prototype = {
7176         /**
7177          * The element's default display mode  (defaults to "") 
7178          * @type String
7179          */
7180         originalDisplay : "",
7181
7182         
7183         // note this is overridden in BS version..
7184         visibilityMode : 1, 
7185         /**
7186          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7187          * @type String
7188          */
7189         defaultUnit : "px",
7190         
7191         /**
7192          * Sets the element's visibility mode. When setVisible() is called it
7193          * will use this to determine whether to set the visibility or the display property.
7194          * @param visMode Element.VISIBILITY or Element.DISPLAY
7195          * @return {Roo.Element} this
7196          */
7197         setVisibilityMode : function(visMode){
7198             this.visibilityMode = visMode;
7199             return this;
7200         },
7201         /**
7202          * Convenience method for setVisibilityMode(Element.DISPLAY)
7203          * @param {String} display (optional) What to set display to when visible
7204          * @return {Roo.Element} this
7205          */
7206         enableDisplayMode : function(display){
7207             this.setVisibilityMode(El.DISPLAY);
7208             if(typeof display != "undefined") { this.originalDisplay = display; }
7209             return this;
7210         },
7211
7212         /**
7213          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7214          * @param {String} selector The simple selector to test
7215          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7216                 search as a number or element (defaults to 10 || document.body)
7217          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7218          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7219          */
7220         findParent : function(simpleSelector, maxDepth, returnEl){
7221             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7222             maxDepth = maxDepth || 50;
7223             if(typeof maxDepth != "number"){
7224                 stopEl = Roo.getDom(maxDepth);
7225                 maxDepth = 10;
7226             }
7227             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7228                 if(dq.is(p, simpleSelector)){
7229                     return returnEl ? Roo.get(p) : p;
7230                 }
7231                 depth++;
7232                 p = p.parentNode;
7233             }
7234             return null;
7235         },
7236
7237
7238         /**
7239          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7240          * @param {String} selector The simple selector to test
7241          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7242                 search as a number or element (defaults to 10 || document.body)
7243          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7244          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7245          */
7246         findParentNode : function(simpleSelector, maxDepth, returnEl){
7247             var p = Roo.fly(this.dom.parentNode, '_internal');
7248             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7249         },
7250         
7251         /**
7252          * Looks at  the scrollable parent element
7253          */
7254         findScrollableParent : function()
7255         {
7256             var overflowRegex = /(auto|scroll)/;
7257             
7258             if(this.getStyle('position') === 'fixed'){
7259                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7260             }
7261             
7262             var excludeStaticParent = this.getStyle('position') === "absolute";
7263             
7264             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7265                 
7266                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7267                     continue;
7268                 }
7269                 
7270                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7271                     return parent;
7272                 }
7273                 
7274                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7275                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7276                 }
7277             }
7278             
7279             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7280         },
7281
7282         /**
7283          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7284          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7285          * @param {String} selector The simple selector to test
7286          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7287                 search as a number or element (defaults to 10 || document.body)
7288          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7289          */
7290         up : function(simpleSelector, maxDepth){
7291             return this.findParentNode(simpleSelector, maxDepth, true);
7292         },
7293
7294
7295
7296         /**
7297          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7298          * @param {String} selector The simple selector to test
7299          * @return {Boolean} True if this element matches the selector, else false
7300          */
7301         is : function(simpleSelector){
7302             return Roo.DomQuery.is(this.dom, simpleSelector);
7303         },
7304
7305         /**
7306          * Perform animation on this element.
7307          * @param {Object} args The YUI animation control args
7308          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7309          * @param {Function} onComplete (optional) Function to call when animation completes
7310          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7311          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7312          * @return {Roo.Element} this
7313          */
7314         animate : function(args, duration, onComplete, easing, animType){
7315             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7316             return this;
7317         },
7318
7319         /*
7320          * @private Internal animation call
7321          */
7322         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7323             animType = animType || 'run';
7324             opt = opt || {};
7325             var anim = Roo.lib.Anim[animType](
7326                 this.dom, args,
7327                 (opt.duration || defaultDur) || .35,
7328                 (opt.easing || defaultEase) || 'easeOut',
7329                 function(){
7330                     Roo.callback(cb, this);
7331                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7332                 },
7333                 this
7334             );
7335             opt.anim = anim;
7336             return anim;
7337         },
7338
7339         // private legacy anim prep
7340         preanim : function(a, i){
7341             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7342         },
7343
7344         /**
7345          * Removes worthless text nodes
7346          * @param {Boolean} forceReclean (optional) By default the element
7347          * keeps track if it has been cleaned already so
7348          * you can call this over and over. However, if you update the element and
7349          * need to force a reclean, you can pass true.
7350          */
7351         clean : function(forceReclean){
7352             if(this.isCleaned && forceReclean !== true){
7353                 return this;
7354             }
7355             var ns = /\S/;
7356             var d = this.dom, n = d.firstChild, ni = -1;
7357             while(n){
7358                 var nx = n.nextSibling;
7359                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7360                     d.removeChild(n);
7361                 }else{
7362                     n.nodeIndex = ++ni;
7363                 }
7364                 n = nx;
7365             }
7366             this.isCleaned = true;
7367             return this;
7368         },
7369
7370         // private
7371         calcOffsetsTo : function(el){
7372             el = Roo.get(el);
7373             var d = el.dom;
7374             var restorePos = false;
7375             if(el.getStyle('position') == 'static'){
7376                 el.position('relative');
7377                 restorePos = true;
7378             }
7379             var x = 0, y =0;
7380             var op = this.dom;
7381             while(op && op != d && op.tagName != 'HTML'){
7382                 x+= op.offsetLeft;
7383                 y+= op.offsetTop;
7384                 op = op.offsetParent;
7385             }
7386             if(restorePos){
7387                 el.position('static');
7388             }
7389             return [x, y];
7390         },
7391
7392         /**
7393          * Scrolls this element into view within the passed container.
7394          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7395          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7396          * @return {Roo.Element} this
7397          */
7398         scrollIntoView : function(container, hscroll){
7399             var c = Roo.getDom(container) || document.body;
7400             var el = this.dom;
7401
7402             var o = this.calcOffsetsTo(c),
7403                 l = o[0],
7404                 t = o[1],
7405                 b = t+el.offsetHeight,
7406                 r = l+el.offsetWidth;
7407
7408             var ch = c.clientHeight;
7409             var ct = parseInt(c.scrollTop, 10);
7410             var cl = parseInt(c.scrollLeft, 10);
7411             var cb = ct + ch;
7412             var cr = cl + c.clientWidth;
7413
7414             if(t < ct){
7415                 c.scrollTop = t;
7416             }else if(b > cb){
7417                 c.scrollTop = b-ch;
7418             }
7419
7420             if(hscroll !== false){
7421                 if(l < cl){
7422                     c.scrollLeft = l;
7423                 }else if(r > cr){
7424                     c.scrollLeft = r-c.clientWidth;
7425                 }
7426             }
7427             return this;
7428         },
7429
7430         // private
7431         scrollChildIntoView : function(child, hscroll){
7432             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7433         },
7434
7435         /**
7436          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7437          * the new height may not be available immediately.
7438          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7439          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7440          * @param {Function} onComplete (optional) Function to call when animation completes
7441          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7442          * @return {Roo.Element} this
7443          */
7444         autoHeight : function(animate, duration, onComplete, easing){
7445             var oldHeight = this.getHeight();
7446             this.clip();
7447             this.setHeight(1); // force clipping
7448             setTimeout(function(){
7449                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7450                 if(!animate){
7451                     this.setHeight(height);
7452                     this.unclip();
7453                     if(typeof onComplete == "function"){
7454                         onComplete();
7455                     }
7456                 }else{
7457                     this.setHeight(oldHeight); // restore original height
7458                     this.setHeight(height, animate, duration, function(){
7459                         this.unclip();
7460                         if(typeof onComplete == "function") { onComplete(); }
7461                     }.createDelegate(this), easing);
7462                 }
7463             }.createDelegate(this), 0);
7464             return this;
7465         },
7466
7467         /**
7468          * Returns true if this element is an ancestor of the passed element
7469          * @param {HTMLElement/String} el The element to check
7470          * @return {Boolean} True if this element is an ancestor of el, else false
7471          */
7472         contains : function(el){
7473             if(!el){return false;}
7474             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7475         },
7476
7477         /**
7478          * Checks whether the element is currently visible using both visibility and display properties.
7479          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7480          * @return {Boolean} True if the element is currently visible, else false
7481          */
7482         isVisible : function(deep) {
7483             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7484             if(deep !== true || !vis){
7485                 return vis;
7486             }
7487             var p = this.dom.parentNode;
7488             while(p && p.tagName.toLowerCase() != "body"){
7489                 if(!Roo.fly(p, '_isVisible').isVisible()){
7490                     return false;
7491                 }
7492                 p = p.parentNode;
7493             }
7494             return true;
7495         },
7496
7497         /**
7498          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7499          * @param {String} selector The CSS selector
7500          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7501          * @return {CompositeElement/CompositeElementLite} The composite element
7502          */
7503         select : function(selector, unique){
7504             return El.select(selector, unique, this.dom);
7505         },
7506
7507         /**
7508          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7509          * @param {String} selector The CSS selector
7510          * @return {Array} An array of the matched nodes
7511          */
7512         query : function(selector, unique){
7513             return Roo.DomQuery.select(selector, this.dom);
7514         },
7515
7516         /**
7517          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7518          * @param {String} selector The CSS selector
7519          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7520          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7521          */
7522         child : function(selector, returnDom){
7523             var n = Roo.DomQuery.selectNode(selector, this.dom);
7524             return returnDom ? n : Roo.get(n);
7525         },
7526
7527         /**
7528          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7529          * @param {String} selector The CSS selector
7530          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7531          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7532          */
7533         down : function(selector, returnDom){
7534             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7535             return returnDom ? n : Roo.get(n);
7536         },
7537
7538         /**
7539          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7540          * @param {String} group The group the DD object is member of
7541          * @param {Object} config The DD config object
7542          * @param {Object} overrides An object containing methods to override/implement on the DD object
7543          * @return {Roo.dd.DD} The DD object
7544          */
7545         initDD : function(group, config, overrides){
7546             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7547             return Roo.apply(dd, overrides);
7548         },
7549
7550         /**
7551          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7552          * @param {String} group The group the DDProxy object is member of
7553          * @param {Object} config The DDProxy config object
7554          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7555          * @return {Roo.dd.DDProxy} The DDProxy object
7556          */
7557         initDDProxy : function(group, config, overrides){
7558             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7559             return Roo.apply(dd, overrides);
7560         },
7561
7562         /**
7563          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7564          * @param {String} group The group the DDTarget object is member of
7565          * @param {Object} config The DDTarget config object
7566          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7567          * @return {Roo.dd.DDTarget} The DDTarget object
7568          */
7569         initDDTarget : function(group, config, overrides){
7570             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7571             return Roo.apply(dd, overrides);
7572         },
7573
7574         /**
7575          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7576          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7577          * @param {Boolean} visible Whether the element is visible
7578          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7579          * @return {Roo.Element} this
7580          */
7581          setVisible : function(visible, animate){
7582             if(!animate || !A){
7583                 if(this.visibilityMode == El.DISPLAY){
7584                     this.setDisplayed(visible);
7585                 }else{
7586                     this.fixDisplay();
7587                     this.dom.style.visibility = visible ? "visible" : "hidden";
7588                 }
7589             }else{
7590                 // closure for composites
7591                 var dom = this.dom;
7592                 var visMode = this.visibilityMode;
7593                 if(visible){
7594                     this.setOpacity(.01);
7595                     this.setVisible(true);
7596                 }
7597                 this.anim({opacity: { to: (visible?1:0) }},
7598                       this.preanim(arguments, 1),
7599                       null, .35, 'easeIn', function(){
7600                          if(!visible){
7601                              if(visMode == El.DISPLAY){
7602                                  dom.style.display = "none";
7603                              }else{
7604                                  dom.style.visibility = "hidden";
7605                              }
7606                              Roo.get(dom).setOpacity(1);
7607                          }
7608                      });
7609             }
7610             return this;
7611         },
7612
7613         /**
7614          * Returns true if display is not "none"
7615          * @return {Boolean}
7616          */
7617         isDisplayed : function() {
7618             return this.getStyle("display") != "none";
7619         },
7620
7621         /**
7622          * Toggles the element's visibility or display, depending on visibility mode.
7623          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7624          * @return {Roo.Element} this
7625          */
7626         toggle : function(animate){
7627             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7628             return this;
7629         },
7630
7631         /**
7632          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7633          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7634          * @return {Roo.Element} this
7635          */
7636         setDisplayed : function(value) {
7637             if(typeof value == "boolean"){
7638                value = value ? this.originalDisplay : "none";
7639             }
7640             this.setStyle("display", value);
7641             return this;
7642         },
7643
7644         /**
7645          * Tries to focus the element. Any exceptions are caught and ignored.
7646          * @return {Roo.Element} this
7647          */
7648         focus : function() {
7649             try{
7650                 this.dom.focus();
7651             }catch(e){}
7652             return this;
7653         },
7654
7655         /**
7656          * Tries to blur the element. Any exceptions are caught and ignored.
7657          * @return {Roo.Element} this
7658          */
7659         blur : function() {
7660             try{
7661                 this.dom.blur();
7662             }catch(e){}
7663             return this;
7664         },
7665
7666         /**
7667          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7668          * @param {String/Array} className The CSS class to add, or an array of classes
7669          * @return {Roo.Element} this
7670          */
7671         addClass : function(className){
7672             if(className instanceof Array){
7673                 for(var i = 0, len = className.length; i < len; i++) {
7674                     this.addClass(className[i]);
7675                 }
7676             }else{
7677                 if(className && !this.hasClass(className)){
7678                     if (this.dom instanceof SVGElement) {
7679                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
7680                     } else {
7681                         this.dom.className = this.dom.className + " " + className;
7682                     }
7683                 }
7684             }
7685             return this;
7686         },
7687
7688         /**
7689          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7690          * @param {String/Array} className The CSS class to add, or an array of classes
7691          * @return {Roo.Element} this
7692          */
7693         radioClass : function(className){
7694             var siblings = this.dom.parentNode.childNodes;
7695             for(var i = 0; i < siblings.length; i++) {
7696                 var s = siblings[i];
7697                 if(s.nodeType == 1){
7698                     Roo.get(s).removeClass(className);
7699                 }
7700             }
7701             this.addClass(className);
7702             return this;
7703         },
7704
7705         /**
7706          * Removes one or more CSS classes from the element.
7707          * @param {String/Array} className The CSS class to remove, or an array of classes
7708          * @return {Roo.Element} this
7709          */
7710         removeClass : function(className){
7711             
7712             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
7713             if(!className || !cn){
7714                 return this;
7715             }
7716             if(className instanceof Array){
7717                 for(var i = 0, len = className.length; i < len; i++) {
7718                     this.removeClass(className[i]);
7719                 }
7720             }else{
7721                 if(this.hasClass(className)){
7722                     var re = this.classReCache[className];
7723                     if (!re) {
7724                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7725                        this.classReCache[className] = re;
7726                     }
7727                     if (this.dom instanceof SVGElement) {
7728                         this.dom.className.baseVal = cn.replace(re, " ");
7729                     } else {
7730                         this.dom.className = cn.replace(re, " ");
7731                     }
7732                 }
7733             }
7734             return this;
7735         },
7736
7737         // private
7738         classReCache: {},
7739
7740         /**
7741          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7742          * @param {String} className The CSS class to toggle
7743          * @return {Roo.Element} this
7744          */
7745         toggleClass : function(className){
7746             if(this.hasClass(className)){
7747                 this.removeClass(className);
7748             }else{
7749                 this.addClass(className);
7750             }
7751             return this;
7752         },
7753
7754         /**
7755          * Checks if the specified CSS class exists on this element's DOM node.
7756          * @param {String} className The CSS class to check for
7757          * @return {Boolean} True if the class exists, else false
7758          */
7759         hasClass : function(className){
7760             if (this.dom instanceof SVGElement) {
7761                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
7762             } 
7763             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7764         },
7765
7766         /**
7767          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7768          * @param {String} oldClassName The CSS class to replace
7769          * @param {String} newClassName The replacement CSS class
7770          * @return {Roo.Element} this
7771          */
7772         replaceClass : function(oldClassName, newClassName){
7773             this.removeClass(oldClassName);
7774             this.addClass(newClassName);
7775             return this;
7776         },
7777
7778         /**
7779          * Returns an object with properties matching the styles requested.
7780          * For example, el.getStyles('color', 'font-size', 'width') might return
7781          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7782          * @param {String} style1 A style name
7783          * @param {String} style2 A style name
7784          * @param {String} etc.
7785          * @return {Object} The style object
7786          */
7787         getStyles : function(){
7788             var a = arguments, len = a.length, r = {};
7789             for(var i = 0; i < len; i++){
7790                 r[a[i]] = this.getStyle(a[i]);
7791             }
7792             return r;
7793         },
7794
7795         /**
7796          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7797          * @param {String} property The style property whose value is returned.
7798          * @return {String} The current value of the style property for this element.
7799          */
7800         getStyle : function(){
7801             return view && view.getComputedStyle ?
7802                 function(prop){
7803                     var el = this.dom, v, cs, camel;
7804                     if(prop == 'float'){
7805                         prop = "cssFloat";
7806                     }
7807                     if(el.style && (v = el.style[prop])){
7808                         return v;
7809                     }
7810                     if(cs = view.getComputedStyle(el, "")){
7811                         if(!(camel = propCache[prop])){
7812                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7813                         }
7814                         return cs[camel];
7815                     }
7816                     return null;
7817                 } :
7818                 function(prop){
7819                     var el = this.dom, v, cs, camel;
7820                     if(prop == 'opacity'){
7821                         if(typeof el.style.filter == 'string'){
7822                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7823                             if(m){
7824                                 var fv = parseFloat(m[1]);
7825                                 if(!isNaN(fv)){
7826                                     return fv ? fv / 100 : 0;
7827                                 }
7828                             }
7829                         }
7830                         return 1;
7831                     }else if(prop == 'float'){
7832                         prop = "styleFloat";
7833                     }
7834                     if(!(camel = propCache[prop])){
7835                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7836                     }
7837                     if(v = el.style[camel]){
7838                         return v;
7839                     }
7840                     if(cs = el.currentStyle){
7841                         return cs[camel];
7842                     }
7843                     return null;
7844                 };
7845         }(),
7846
7847         /**
7848          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7849          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7850          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7851          * @return {Roo.Element} this
7852          */
7853         setStyle : function(prop, value){
7854             if(typeof prop == "string"){
7855                 
7856                 if (prop == 'float') {
7857                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7858                     return this;
7859                 }
7860                 
7861                 var camel;
7862                 if(!(camel = propCache[prop])){
7863                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7864                 }
7865                 
7866                 if(camel == 'opacity') {
7867                     this.setOpacity(value);
7868                 }else{
7869                     this.dom.style[camel] = value;
7870                 }
7871             }else{
7872                 for(var style in prop){
7873                     if(typeof prop[style] != "function"){
7874                        this.setStyle(style, prop[style]);
7875                     }
7876                 }
7877             }
7878             return this;
7879         },
7880
7881         /**
7882          * More flexible version of {@link #setStyle} for setting style properties.
7883          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7884          * a function which returns such a specification.
7885          * @return {Roo.Element} this
7886          */
7887         applyStyles : function(style){
7888             Roo.DomHelper.applyStyles(this.dom, style);
7889             return this;
7890         },
7891
7892         /**
7893           * 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).
7894           * @return {Number} The X position of the element
7895           */
7896         getX : function(){
7897             return D.getX(this.dom);
7898         },
7899
7900         /**
7901           * 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).
7902           * @return {Number} The Y position of the element
7903           */
7904         getY : function(){
7905             return D.getY(this.dom);
7906         },
7907
7908         /**
7909           * 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).
7910           * @return {Array} The XY position of the element
7911           */
7912         getXY : function(){
7913             return D.getXY(this.dom);
7914         },
7915
7916         /**
7917          * 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).
7918          * @param {Number} The X position of the element
7919          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7920          * @return {Roo.Element} this
7921          */
7922         setX : function(x, animate){
7923             if(!animate || !A){
7924                 D.setX(this.dom, x);
7925             }else{
7926                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7927             }
7928             return this;
7929         },
7930
7931         /**
7932          * 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).
7933          * @param {Number} The Y position of the element
7934          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7935          * @return {Roo.Element} this
7936          */
7937         setY : function(y, animate){
7938             if(!animate || !A){
7939                 D.setY(this.dom, y);
7940             }else{
7941                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7942             }
7943             return this;
7944         },
7945
7946         /**
7947          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7948          * @param {String} left The left CSS property value
7949          * @return {Roo.Element} this
7950          */
7951         setLeft : function(left){
7952             this.setStyle("left", this.addUnits(left));
7953             return this;
7954         },
7955
7956         /**
7957          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7958          * @param {String} top The top CSS property value
7959          * @return {Roo.Element} this
7960          */
7961         setTop : function(top){
7962             this.setStyle("top", this.addUnits(top));
7963             return this;
7964         },
7965
7966         /**
7967          * Sets the element's CSS right style.
7968          * @param {String} right The right CSS property value
7969          * @return {Roo.Element} this
7970          */
7971         setRight : function(right){
7972             this.setStyle("right", this.addUnits(right));
7973             return this;
7974         },
7975
7976         /**
7977          * Sets the element's CSS bottom style.
7978          * @param {String} bottom The bottom CSS property value
7979          * @return {Roo.Element} this
7980          */
7981         setBottom : function(bottom){
7982             this.setStyle("bottom", this.addUnits(bottom));
7983             return this;
7984         },
7985
7986         /**
7987          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7988          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7989          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7990          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7991          * @return {Roo.Element} this
7992          */
7993         setXY : function(pos, animate){
7994             if(!animate || !A){
7995                 D.setXY(this.dom, pos);
7996             }else{
7997                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7998             }
7999             return this;
8000         },
8001
8002         /**
8003          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8004          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8005          * @param {Number} x X value for new position (coordinates are page-based)
8006          * @param {Number} y Y value for new position (coordinates are page-based)
8007          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8008          * @return {Roo.Element} this
8009          */
8010         setLocation : function(x, y, animate){
8011             this.setXY([x, y], this.preanim(arguments, 2));
8012             return this;
8013         },
8014
8015         /**
8016          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8017          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8018          * @param {Number} x X value for new position (coordinates are page-based)
8019          * @param {Number} y Y value for new position (coordinates are page-based)
8020          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8021          * @return {Roo.Element} this
8022          */
8023         moveTo : function(x, y, animate){
8024             this.setXY([x, y], this.preanim(arguments, 2));
8025             return this;
8026         },
8027
8028         /**
8029          * Returns the region of the given element.
8030          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8031          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8032          */
8033         getRegion : function(){
8034             return D.getRegion(this.dom);
8035         },
8036
8037         /**
8038          * Returns the offset height of the element
8039          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8040          * @return {Number} The element's height
8041          */
8042         getHeight : function(contentHeight){
8043             var h = this.dom.offsetHeight || 0;
8044             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8045         },
8046
8047         /**
8048          * Returns the offset width of the element
8049          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8050          * @return {Number} The element's width
8051          */
8052         getWidth : function(contentWidth){
8053             var w = this.dom.offsetWidth || 0;
8054             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8055         },
8056
8057         /**
8058          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8059          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8060          * if a height has not been set using CSS.
8061          * @return {Number}
8062          */
8063         getComputedHeight : function(){
8064             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8065             if(!h){
8066                 h = parseInt(this.getStyle('height'), 10) || 0;
8067                 if(!this.isBorderBox()){
8068                     h += this.getFrameWidth('tb');
8069                 }
8070             }
8071             return h;
8072         },
8073
8074         /**
8075          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8076          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8077          * if a width has not been set using CSS.
8078          * @return {Number}
8079          */
8080         getComputedWidth : function(){
8081             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8082             if(!w){
8083                 w = parseInt(this.getStyle('width'), 10) || 0;
8084                 if(!this.isBorderBox()){
8085                     w += this.getFrameWidth('lr');
8086                 }
8087             }
8088             return w;
8089         },
8090
8091         /**
8092          * Returns the size of the element.
8093          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8094          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8095          */
8096         getSize : function(contentSize){
8097             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8098         },
8099
8100         /**
8101          * Returns the width and height of the viewport.
8102          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8103          */
8104         getViewSize : function(){
8105             var d = this.dom, doc = document, aw = 0, ah = 0;
8106             if(d == doc || d == doc.body){
8107                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8108             }else{
8109                 return {
8110                     width : d.clientWidth,
8111                     height: d.clientHeight
8112                 };
8113             }
8114         },
8115
8116         /**
8117          * Returns the value of the "value" attribute
8118          * @param {Boolean} asNumber true to parse the value as a number
8119          * @return {String/Number}
8120          */
8121         getValue : function(asNumber){
8122             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8123         },
8124
8125         // private
8126         adjustWidth : function(width){
8127             if(typeof width == "number"){
8128                 if(this.autoBoxAdjust && !this.isBorderBox()){
8129                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8130                 }
8131                 if(width < 0){
8132                     width = 0;
8133                 }
8134             }
8135             return width;
8136         },
8137
8138         // private
8139         adjustHeight : function(height){
8140             if(typeof height == "number"){
8141                if(this.autoBoxAdjust && !this.isBorderBox()){
8142                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8143                }
8144                if(height < 0){
8145                    height = 0;
8146                }
8147             }
8148             return height;
8149         },
8150
8151         /**
8152          * Set the width of the element
8153          * @param {Number} width The new width
8154          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8155          * @return {Roo.Element} this
8156          */
8157         setWidth : function(width, animate){
8158             width = this.adjustWidth(width);
8159             if(!animate || !A){
8160                 this.dom.style.width = this.addUnits(width);
8161             }else{
8162                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8163             }
8164             return this;
8165         },
8166
8167         /**
8168          * Set the height of the element
8169          * @param {Number} height The new height
8170          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8171          * @return {Roo.Element} this
8172          */
8173          setHeight : function(height, animate){
8174             height = this.adjustHeight(height);
8175             if(!animate || !A){
8176                 this.dom.style.height = this.addUnits(height);
8177             }else{
8178                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8179             }
8180             return this;
8181         },
8182
8183         /**
8184          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8185          * @param {Number} width The new width
8186          * @param {Number} height The new height
8187          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8188          * @return {Roo.Element} this
8189          */
8190          setSize : function(width, height, animate){
8191             if(typeof width == "object"){ // in case of object from getSize()
8192                 height = width.height; width = width.width;
8193             }
8194             width = this.adjustWidth(width); height = this.adjustHeight(height);
8195             if(!animate || !A){
8196                 this.dom.style.width = this.addUnits(width);
8197                 this.dom.style.height = this.addUnits(height);
8198             }else{
8199                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8200             }
8201             return this;
8202         },
8203
8204         /**
8205          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8206          * @param {Number} x X value for new position (coordinates are page-based)
8207          * @param {Number} y Y value for new position (coordinates are page-based)
8208          * @param {Number} width The new width
8209          * @param {Number} height The new height
8210          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8211          * @return {Roo.Element} this
8212          */
8213         setBounds : function(x, y, width, height, animate){
8214             if(!animate || !A){
8215                 this.setSize(width, height);
8216                 this.setLocation(x, y);
8217             }else{
8218                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8219                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8220                               this.preanim(arguments, 4), 'motion');
8221             }
8222             return this;
8223         },
8224
8225         /**
8226          * 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.
8227          * @param {Roo.lib.Region} region The region to fill
8228          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8229          * @return {Roo.Element} this
8230          */
8231         setRegion : function(region, animate){
8232             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8233             return this;
8234         },
8235
8236         /**
8237          * Appends an event handler
8238          *
8239          * @param {String}   eventName     The type of event to append
8240          * @param {Function} fn        The method the event invokes
8241          * @param {Object} scope       (optional) The scope (this object) of the fn
8242          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8243          */
8244         addListener : function(eventName, fn, scope, options){
8245             if (this.dom) {
8246                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8247             }
8248             if (eventName == 'dblclick') {
8249                 this.addListener('touchstart', this.onTapHandler, this);
8250             }
8251         },
8252         tapedTwice : false,
8253         onTapHandler : function(event)
8254         {
8255             if(!this.tapedTwice) {
8256                 this.tapedTwice = true;
8257                 var s = this;
8258                 setTimeout( function() {
8259                     s.tapedTwice = false;
8260                 }, 300 );
8261                 return;
8262             }
8263             event.preventDefault();
8264             var revent = new MouseEvent('dblclick',  {
8265                 view: window,
8266                 bubbles: true,
8267                 cancelable: true
8268             });
8269              
8270             this.dom.dispatchEvent(revent);
8271             //action on double tap goes below
8272              
8273         }, 
8274
8275         /**
8276          * Removes an event handler from this element
8277          * @param {String} eventName the type of event to remove
8278          * @param {Function} fn the method the event invokes
8279          * @return {Roo.Element} this
8280          */
8281         removeListener : function(eventName, fn){
8282             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8283             return this;
8284         },
8285
8286         /**
8287          * Removes all previous added listeners from this element
8288          * @return {Roo.Element} this
8289          */
8290         removeAllListeners : function(){
8291             E.purgeElement(this.dom);
8292             return this;
8293         },
8294
8295         relayEvent : function(eventName, observable){
8296             this.on(eventName, function(e){
8297                 observable.fireEvent(eventName, e);
8298             });
8299         },
8300
8301         /**
8302          * Set the opacity of the element
8303          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8304          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8305          * @return {Roo.Element} this
8306          */
8307          setOpacity : function(opacity, animate){
8308             if(!animate || !A){
8309                 var s = this.dom.style;
8310                 if(Roo.isIE){
8311                     s.zoom = 1;
8312                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8313                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8314                 }else{
8315                     s.opacity = opacity;
8316                 }
8317             }else{
8318                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8319             }
8320             return this;
8321         },
8322
8323         /**
8324          * Gets the left X coordinate
8325          * @param {Boolean} local True to get the local css position instead of page coordinate
8326          * @return {Number}
8327          */
8328         getLeft : function(local){
8329             if(!local){
8330                 return this.getX();
8331             }else{
8332                 return parseInt(this.getStyle("left"), 10) || 0;
8333             }
8334         },
8335
8336         /**
8337          * Gets the right X coordinate of the element (element X position + element width)
8338          * @param {Boolean} local True to get the local css position instead of page coordinate
8339          * @return {Number}
8340          */
8341         getRight : function(local){
8342             if(!local){
8343                 return this.getX() + this.getWidth();
8344             }else{
8345                 return (this.getLeft(true) + this.getWidth()) || 0;
8346             }
8347         },
8348
8349         /**
8350          * Gets the top Y coordinate
8351          * @param {Boolean} local True to get the local css position instead of page coordinate
8352          * @return {Number}
8353          */
8354         getTop : function(local) {
8355             if(!local){
8356                 return this.getY();
8357             }else{
8358                 return parseInt(this.getStyle("top"), 10) || 0;
8359             }
8360         },
8361
8362         /**
8363          * Gets the bottom Y coordinate of the element (element Y position + element height)
8364          * @param {Boolean} local True to get the local css position instead of page coordinate
8365          * @return {Number}
8366          */
8367         getBottom : function(local){
8368             if(!local){
8369                 return this.getY() + this.getHeight();
8370             }else{
8371                 return (this.getTop(true) + this.getHeight()) || 0;
8372             }
8373         },
8374
8375         /**
8376         * Initializes positioning on this element. If a desired position is not passed, it will make the
8377         * the element positioned relative IF it is not already positioned.
8378         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8379         * @param {Number} zIndex (optional) The zIndex to apply
8380         * @param {Number} x (optional) Set the page X position
8381         * @param {Number} y (optional) Set the page Y position
8382         */
8383         position : function(pos, zIndex, x, y){
8384             if(!pos){
8385                if(this.getStyle('position') == 'static'){
8386                    this.setStyle('position', 'relative');
8387                }
8388             }else{
8389                 this.setStyle("position", pos);
8390             }
8391             if(zIndex){
8392                 this.setStyle("z-index", zIndex);
8393             }
8394             if(x !== undefined && y !== undefined){
8395                 this.setXY([x, y]);
8396             }else if(x !== undefined){
8397                 this.setX(x);
8398             }else if(y !== undefined){
8399                 this.setY(y);
8400             }
8401         },
8402
8403         /**
8404         * Clear positioning back to the default when the document was loaded
8405         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8406         * @return {Roo.Element} this
8407          */
8408         clearPositioning : function(value){
8409             value = value ||'';
8410             this.setStyle({
8411                 "left": value,
8412                 "right": value,
8413                 "top": value,
8414                 "bottom": value,
8415                 "z-index": "",
8416                 "position" : "static"
8417             });
8418             return this;
8419         },
8420
8421         /**
8422         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8423         * snapshot before performing an update and then restoring the element.
8424         * @return {Object}
8425         */
8426         getPositioning : function(){
8427             var l = this.getStyle("left");
8428             var t = this.getStyle("top");
8429             return {
8430                 "position" : this.getStyle("position"),
8431                 "left" : l,
8432                 "right" : l ? "" : this.getStyle("right"),
8433                 "top" : t,
8434                 "bottom" : t ? "" : this.getStyle("bottom"),
8435                 "z-index" : this.getStyle("z-index")
8436             };
8437         },
8438
8439         /**
8440          * Gets the width of the border(s) for the specified side(s)
8441          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8442          * passing lr would get the border (l)eft width + the border (r)ight width.
8443          * @return {Number} The width of the sides passed added together
8444          */
8445         getBorderWidth : function(side){
8446             return this.addStyles(side, El.borders);
8447         },
8448
8449         /**
8450          * Gets the width of the padding(s) for the specified side(s)
8451          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8452          * passing lr would get the padding (l)eft + the padding (r)ight.
8453          * @return {Number} The padding of the sides passed added together
8454          */
8455         getPadding : function(side){
8456             return this.addStyles(side, El.paddings);
8457         },
8458
8459         /**
8460         * Set positioning with an object returned by getPositioning().
8461         * @param {Object} posCfg
8462         * @return {Roo.Element} this
8463          */
8464         setPositioning : function(pc){
8465             this.applyStyles(pc);
8466             if(pc.right == "auto"){
8467                 this.dom.style.right = "";
8468             }
8469             if(pc.bottom == "auto"){
8470                 this.dom.style.bottom = "";
8471             }
8472             return this;
8473         },
8474
8475         // private
8476         fixDisplay : function(){
8477             if(this.getStyle("display") == "none"){
8478                 this.setStyle("visibility", "hidden");
8479                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8480                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8481                     this.setStyle("display", "block");
8482                 }
8483             }
8484         },
8485
8486         /**
8487          * Quick set left and top adding default units
8488          * @param {String} left The left CSS property value
8489          * @param {String} top The top CSS property value
8490          * @return {Roo.Element} this
8491          */
8492          setLeftTop : function(left, top){
8493             this.dom.style.left = this.addUnits(left);
8494             this.dom.style.top = this.addUnits(top);
8495             return this;
8496         },
8497
8498         /**
8499          * Move this element relative to its current position.
8500          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8501          * @param {Number} distance How far to move the element in pixels
8502          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8503          * @return {Roo.Element} this
8504          */
8505          move : function(direction, distance, animate){
8506             var xy = this.getXY();
8507             direction = direction.toLowerCase();
8508             switch(direction){
8509                 case "l":
8510                 case "left":
8511                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8512                     break;
8513                case "r":
8514                case "right":
8515                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8516                     break;
8517                case "t":
8518                case "top":
8519                case "up":
8520                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8521                     break;
8522                case "b":
8523                case "bottom":
8524                case "down":
8525                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8526                     break;
8527             }
8528             return this;
8529         },
8530
8531         /**
8532          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8533          * @return {Roo.Element} this
8534          */
8535         clip : function(){
8536             if(!this.isClipped){
8537                this.isClipped = true;
8538                this.originalClip = {
8539                    "o": this.getStyle("overflow"),
8540                    "x": this.getStyle("overflow-x"),
8541                    "y": this.getStyle("overflow-y")
8542                };
8543                this.setStyle("overflow", "hidden");
8544                this.setStyle("overflow-x", "hidden");
8545                this.setStyle("overflow-y", "hidden");
8546             }
8547             return this;
8548         },
8549
8550         /**
8551          *  Return clipping (overflow) to original clipping before clip() was called
8552          * @return {Roo.Element} this
8553          */
8554         unclip : function(){
8555             if(this.isClipped){
8556                 this.isClipped = false;
8557                 var o = this.originalClip;
8558                 if(o.o){this.setStyle("overflow", o.o);}
8559                 if(o.x){this.setStyle("overflow-x", o.x);}
8560                 if(o.y){this.setStyle("overflow-y", o.y);}
8561             }
8562             return this;
8563         },
8564
8565
8566         /**
8567          * Gets the x,y coordinates specified by the anchor position on the element.
8568          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8569          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8570          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8571          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8572          * @return {Array} [x, y] An array containing the element's x and y coordinates
8573          */
8574         getAnchorXY : function(anchor, local, s){
8575             //Passing a different size is useful for pre-calculating anchors,
8576             //especially for anchored animations that change the el size.
8577
8578             var w, h, vp = false;
8579             if(!s){
8580                 var d = this.dom;
8581                 if(d == document.body || d == document){
8582                     vp = true;
8583                     w = D.getViewWidth(); h = D.getViewHeight();
8584                 }else{
8585                     w = this.getWidth(); h = this.getHeight();
8586                 }
8587             }else{
8588                 w = s.width;  h = s.height;
8589             }
8590             var x = 0, y = 0, r = Math.round;
8591             switch((anchor || "tl").toLowerCase()){
8592                 case "c":
8593                     x = r(w*.5);
8594                     y = r(h*.5);
8595                 break;
8596                 case "t":
8597                     x = r(w*.5);
8598                     y = 0;
8599                 break;
8600                 case "l":
8601                     x = 0;
8602                     y = r(h*.5);
8603                 break;
8604                 case "r":
8605                     x = w;
8606                     y = r(h*.5);
8607                 break;
8608                 case "b":
8609                     x = r(w*.5);
8610                     y = h;
8611                 break;
8612                 case "tl":
8613                     x = 0;
8614                     y = 0;
8615                 break;
8616                 case "bl":
8617                     x = 0;
8618                     y = h;
8619                 break;
8620                 case "br":
8621                     x = w;
8622                     y = h;
8623                 break;
8624                 case "tr":
8625                     x = w;
8626                     y = 0;
8627                 break;
8628             }
8629             if(local === true){
8630                 return [x, y];
8631             }
8632             if(vp){
8633                 var sc = this.getScroll();
8634                 return [x + sc.left, y + sc.top];
8635             }
8636             //Add the element's offset xy
8637             var o = this.getXY();
8638             return [x+o[0], y+o[1]];
8639         },
8640
8641         /**
8642          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8643          * supported position values.
8644          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8645          * @param {String} position The position to align to.
8646          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8647          * @return {Array} [x, y]
8648          */
8649         getAlignToXY : function(el, p, o)
8650         {
8651             el = Roo.get(el);
8652             var d = this.dom;
8653             if(!el.dom){
8654                 throw "Element.alignTo with an element that doesn't exist";
8655             }
8656             var c = false; //constrain to viewport
8657             var p1 = "", p2 = "";
8658             o = o || [0,0];
8659
8660             if(!p){
8661                 p = "tl-bl";
8662             }else if(p == "?"){
8663                 p = "tl-bl?";
8664             }else if(p.indexOf("-") == -1){
8665                 p = "tl-" + p;
8666             }
8667             p = p.toLowerCase();
8668             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8669             if(!m){
8670                throw "Element.alignTo with an invalid alignment " + p;
8671             }
8672             p1 = m[1]; p2 = m[2]; c = !!m[3];
8673
8674             //Subtract the aligned el's internal xy from the target's offset xy
8675             //plus custom offset to get the aligned el's new offset xy
8676             var a1 = this.getAnchorXY(p1, true);
8677             var a2 = el.getAnchorXY(p2, false);
8678             var x = a2[0] - a1[0] + o[0];
8679             var y = a2[1] - a1[1] + o[1];
8680             if(c){
8681                 //constrain the aligned el to viewport if necessary
8682                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8683                 // 5px of margin for ie
8684                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8685
8686                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8687                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8688                 //otherwise swap the aligned el to the opposite border of the target.
8689                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8690                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8691                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8692                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8693
8694                var doc = document;
8695                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8696                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8697
8698                if((x+w) > dw + scrollX){
8699                     x = swapX ? r.left-w : dw+scrollX-w;
8700                 }
8701                if(x < scrollX){
8702                    x = swapX ? r.right : scrollX;
8703                }
8704                if((y+h) > dh + scrollY){
8705                     y = swapY ? r.top-h : dh+scrollY-h;
8706                 }
8707                if (y < scrollY){
8708                    y = swapY ? r.bottom : scrollY;
8709                }
8710             }
8711             return [x,y];
8712         },
8713
8714         // private
8715         getConstrainToXY : function(){
8716             var os = {top:0, left:0, bottom:0, right: 0};
8717
8718             return function(el, local, offsets, proposedXY){
8719                 el = Roo.get(el);
8720                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8721
8722                 var vw, vh, vx = 0, vy = 0;
8723                 if(el.dom == document.body || el.dom == document){
8724                     vw = Roo.lib.Dom.getViewWidth();
8725                     vh = Roo.lib.Dom.getViewHeight();
8726                 }else{
8727                     vw = el.dom.clientWidth;
8728                     vh = el.dom.clientHeight;
8729                     if(!local){
8730                         var vxy = el.getXY();
8731                         vx = vxy[0];
8732                         vy = vxy[1];
8733                     }
8734                 }
8735
8736                 var s = el.getScroll();
8737
8738                 vx += offsets.left + s.left;
8739                 vy += offsets.top + s.top;
8740
8741                 vw -= offsets.right;
8742                 vh -= offsets.bottom;
8743
8744                 var vr = vx+vw;
8745                 var vb = vy+vh;
8746
8747                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8748                 var x = xy[0], y = xy[1];
8749                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8750
8751                 // only move it if it needs it
8752                 var moved = false;
8753
8754                 // first validate right/bottom
8755                 if((x + w) > vr){
8756                     x = vr - w;
8757                     moved = true;
8758                 }
8759                 if((y + h) > vb){
8760                     y = vb - h;
8761                     moved = true;
8762                 }
8763                 // then make sure top/left isn't negative
8764                 if(x < vx){
8765                     x = vx;
8766                     moved = true;
8767                 }
8768                 if(y < vy){
8769                     y = vy;
8770                     moved = true;
8771                 }
8772                 return moved ? [x, y] : false;
8773             };
8774         }(),
8775
8776         // private
8777         adjustForConstraints : function(xy, parent, offsets){
8778             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8779         },
8780
8781         /**
8782          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8783          * document it aligns it to the viewport.
8784          * The position parameter is optional, and can be specified in any one of the following formats:
8785          * <ul>
8786          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8787          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8788          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8789          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8790          *   <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
8791          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8792          * </ul>
8793          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8794          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8795          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8796          * that specified in order to enforce the viewport constraints.
8797          * Following are all of the supported anchor positions:
8798     <pre>
8799     Value  Description
8800     -----  -----------------------------
8801     tl     The top left corner (default)
8802     t      The center of the top edge
8803     tr     The top right corner
8804     l      The center of the left edge
8805     c      In the center of the element
8806     r      The center of the right edge
8807     bl     The bottom left corner
8808     b      The center of the bottom edge
8809     br     The bottom right corner
8810     </pre>
8811     Example Usage:
8812     <pre><code>
8813     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8814     el.alignTo("other-el");
8815
8816     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8817     el.alignTo("other-el", "tr?");
8818
8819     // align the bottom right corner of el with the center left edge of other-el
8820     el.alignTo("other-el", "br-l?");
8821
8822     // align the center of el with the bottom left corner of other-el and
8823     // adjust the x position by -6 pixels (and the y position by 0)
8824     el.alignTo("other-el", "c-bl", [-6, 0]);
8825     </code></pre>
8826          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8827          * @param {String} position The position to align to.
8828          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8829          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8830          * @return {Roo.Element} this
8831          */
8832         alignTo : function(element, position, offsets, animate){
8833             var xy = this.getAlignToXY(element, position, offsets);
8834             this.setXY(xy, this.preanim(arguments, 3));
8835             return this;
8836         },
8837
8838         /**
8839          * Anchors an element to another element and realigns it when the window is resized.
8840          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8841          * @param {String} position The position to align to.
8842          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8843          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8844          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8845          * is a number, it is used as the buffer delay (defaults to 50ms).
8846          * @param {Function} callback The function to call after the animation finishes
8847          * @return {Roo.Element} this
8848          */
8849         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8850             var action = function(){
8851                 this.alignTo(el, alignment, offsets, animate);
8852                 Roo.callback(callback, this);
8853             };
8854             Roo.EventManager.onWindowResize(action, this);
8855             var tm = typeof monitorScroll;
8856             if(tm != 'undefined'){
8857                 Roo.EventManager.on(window, 'scroll', action, this,
8858                     {buffer: tm == 'number' ? monitorScroll : 50});
8859             }
8860             action.call(this); // align immediately
8861             return this;
8862         },
8863         /**
8864          * Clears any opacity settings from this element. Required in some cases for IE.
8865          * @return {Roo.Element} this
8866          */
8867         clearOpacity : function(){
8868             if (window.ActiveXObject) {
8869                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8870                     this.dom.style.filter = "";
8871                 }
8872             } else {
8873                 this.dom.style.opacity = "";
8874                 this.dom.style["-moz-opacity"] = "";
8875                 this.dom.style["-khtml-opacity"] = "";
8876             }
8877             return this;
8878         },
8879
8880         /**
8881          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8882          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8883          * @return {Roo.Element} this
8884          */
8885         hide : function(animate){
8886             this.setVisible(false, this.preanim(arguments, 0));
8887             return this;
8888         },
8889
8890         /**
8891         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8892         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8893          * @return {Roo.Element} this
8894          */
8895         show : function(animate){
8896             this.setVisible(true, this.preanim(arguments, 0));
8897             return this;
8898         },
8899
8900         /**
8901          * @private Test if size has a unit, otherwise appends the default
8902          */
8903         addUnits : function(size){
8904             return Roo.Element.addUnits(size, this.defaultUnit);
8905         },
8906
8907         /**
8908          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8909          * @return {Roo.Element} this
8910          */
8911         beginMeasure : function(){
8912             var el = this.dom;
8913             if(el.offsetWidth || el.offsetHeight){
8914                 return this; // offsets work already
8915             }
8916             var changed = [];
8917             var p = this.dom, b = document.body; // start with this element
8918             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8919                 var pe = Roo.get(p);
8920                 if(pe.getStyle('display') == 'none'){
8921                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8922                     p.style.visibility = "hidden";
8923                     p.style.display = "block";
8924                 }
8925                 p = p.parentNode;
8926             }
8927             this._measureChanged = changed;
8928             return this;
8929
8930         },
8931
8932         /**
8933          * Restores displays to before beginMeasure was called
8934          * @return {Roo.Element} this
8935          */
8936         endMeasure : function(){
8937             var changed = this._measureChanged;
8938             if(changed){
8939                 for(var i = 0, len = changed.length; i < len; i++) {
8940                     var r = changed[i];
8941                     r.el.style.visibility = r.visibility;
8942                     r.el.style.display = "none";
8943                 }
8944                 this._measureChanged = null;
8945             }
8946             return this;
8947         },
8948
8949         /**
8950         * Update the innerHTML of this element, optionally searching for and processing scripts
8951         * @param {String} html The new HTML
8952         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8953         * @param {Function} callback For async script loading you can be noticed when the update completes
8954         * @return {Roo.Element} this
8955          */
8956         update : function(html, loadScripts, callback){
8957             if(typeof html == "undefined"){
8958                 html = "";
8959             }
8960             if(loadScripts !== true){
8961                 this.dom.innerHTML = html;
8962                 if(typeof callback == "function"){
8963                     callback();
8964                 }
8965                 return this;
8966             }
8967             var id = Roo.id();
8968             var dom = this.dom;
8969
8970             html += '<span id="' + id + '"></span>';
8971
8972             E.onAvailable(id, function(){
8973                 var hd = document.getElementsByTagName("head")[0];
8974                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8975                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8976                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8977
8978                 var match;
8979                 while(match = re.exec(html)){
8980                     var attrs = match[1];
8981                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8982                     if(srcMatch && srcMatch[2]){
8983                        var s = document.createElement("script");
8984                        s.src = srcMatch[2];
8985                        var typeMatch = attrs.match(typeRe);
8986                        if(typeMatch && typeMatch[2]){
8987                            s.type = typeMatch[2];
8988                        }
8989                        hd.appendChild(s);
8990                     }else if(match[2] && match[2].length > 0){
8991                         if(window.execScript) {
8992                            window.execScript(match[2]);
8993                         } else {
8994                             /**
8995                              * eval:var:id
8996                              * eval:var:dom
8997                              * eval:var:html
8998                              * 
8999                              */
9000                            window.eval(match[2]);
9001                         }
9002                     }
9003                 }
9004                 var el = document.getElementById(id);
9005                 if(el){el.parentNode.removeChild(el);}
9006                 if(typeof callback == "function"){
9007                     callback();
9008                 }
9009             });
9010             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9011             return this;
9012         },
9013
9014         /**
9015          * Direct access to the UpdateManager update() method (takes the same parameters).
9016          * @param {String/Function} url The url for this request or a function to call to get the url
9017          * @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}
9018          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9019          * @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.
9020          * @return {Roo.Element} this
9021          */
9022         load : function(){
9023             var um = this.getUpdateManager();
9024             um.update.apply(um, arguments);
9025             return this;
9026         },
9027
9028         /**
9029         * Gets this element's UpdateManager
9030         * @return {Roo.UpdateManager} The UpdateManager
9031         */
9032         getUpdateManager : function(){
9033             if(!this.updateManager){
9034                 this.updateManager = new Roo.UpdateManager(this);
9035             }
9036             return this.updateManager;
9037         },
9038
9039         /**
9040          * Disables text selection for this element (normalized across browsers)
9041          * @return {Roo.Element} this
9042          */
9043         unselectable : function(){
9044             this.dom.unselectable = "on";
9045             this.swallowEvent("selectstart", true);
9046             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9047             this.addClass("x-unselectable");
9048             return this;
9049         },
9050
9051         /**
9052         * Calculates the x, y to center this element on the screen
9053         * @return {Array} The x, y values [x, y]
9054         */
9055         getCenterXY : function(){
9056             return this.getAlignToXY(document, 'c-c');
9057         },
9058
9059         /**
9060         * Centers the Element in either the viewport, or another Element.
9061         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9062         */
9063         center : function(centerIn){
9064             this.alignTo(centerIn || document, 'c-c');
9065             return this;
9066         },
9067
9068         /**
9069          * Tests various css rules/browsers to determine if this element uses a border box
9070          * @return {Boolean}
9071          */
9072         isBorderBox : function(){
9073             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9074         },
9075
9076         /**
9077          * Return a box {x, y, width, height} that can be used to set another elements
9078          * size/location to match this element.
9079          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9080          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9081          * @return {Object} box An object in the format {x, y, width, height}
9082          */
9083         getBox : function(contentBox, local){
9084             var xy;
9085             if(!local){
9086                 xy = this.getXY();
9087             }else{
9088                 var left = parseInt(this.getStyle("left"), 10) || 0;
9089                 var top = parseInt(this.getStyle("top"), 10) || 0;
9090                 xy = [left, top];
9091             }
9092             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9093             if(!contentBox){
9094                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9095             }else{
9096                 var l = this.getBorderWidth("l")+this.getPadding("l");
9097                 var r = this.getBorderWidth("r")+this.getPadding("r");
9098                 var t = this.getBorderWidth("t")+this.getPadding("t");
9099                 var b = this.getBorderWidth("b")+this.getPadding("b");
9100                 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)};
9101             }
9102             bx.right = bx.x + bx.width;
9103             bx.bottom = bx.y + bx.height;
9104             return bx;
9105         },
9106
9107         /**
9108          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9109          for more information about the sides.
9110          * @param {String} sides
9111          * @return {Number}
9112          */
9113         getFrameWidth : function(sides, onlyContentBox){
9114             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9115         },
9116
9117         /**
9118          * 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.
9119          * @param {Object} box The box to fill {x, y, width, height}
9120          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9121          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9122          * @return {Roo.Element} this
9123          */
9124         setBox : function(box, adjust, animate){
9125             var w = box.width, h = box.height;
9126             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9127                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9128                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9129             }
9130             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9131             return this;
9132         },
9133
9134         /**
9135          * Forces the browser to repaint this element
9136          * @return {Roo.Element} this
9137          */
9138          repaint : function(){
9139             var dom = this.dom;
9140             this.addClass("x-repaint");
9141             setTimeout(function(){
9142                 Roo.get(dom).removeClass("x-repaint");
9143             }, 1);
9144             return this;
9145         },
9146
9147         /**
9148          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9149          * then it returns the calculated width of the sides (see getPadding)
9150          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9151          * @return {Object/Number}
9152          */
9153         getMargins : function(side){
9154             if(!side){
9155                 return {
9156                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9157                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9158                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9159                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9160                 };
9161             }else{
9162                 return this.addStyles(side, El.margins);
9163              }
9164         },
9165
9166         // private
9167         addStyles : function(sides, styles){
9168             var val = 0, v, w;
9169             for(var i = 0, len = sides.length; i < len; i++){
9170                 v = this.getStyle(styles[sides.charAt(i)]);
9171                 if(v){
9172                      w = parseInt(v, 10);
9173                      if(w){ val += w; }
9174                 }
9175             }
9176             return val;
9177         },
9178
9179         /**
9180          * Creates a proxy element of this element
9181          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9182          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9183          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9184          * @return {Roo.Element} The new proxy element
9185          */
9186         createProxy : function(config, renderTo, matchBox){
9187             if(renderTo){
9188                 renderTo = Roo.getDom(renderTo);
9189             }else{
9190                 renderTo = document.body;
9191             }
9192             config = typeof config == "object" ?
9193                 config : {tag : "div", cls: config};
9194             var proxy = Roo.DomHelper.append(renderTo, config, true);
9195             if(matchBox){
9196                proxy.setBox(this.getBox());
9197             }
9198             return proxy;
9199         },
9200
9201         /**
9202          * Puts a mask over this element to disable user interaction. Requires core.css.
9203          * This method can only be applied to elements which accept child nodes.
9204          * @param {String} msg (optional) A message to display in the mask
9205          * @param {String} msgCls (optional) A css class to apply to the msg element
9206          * @return {Element} The mask  element
9207          */
9208         mask : function(msg, msgCls)
9209         {
9210             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9211                 this.setStyle("position", "relative");
9212             }
9213             if(!this._mask){
9214                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9215             }
9216             
9217             this.addClass("x-masked");
9218             this._mask.setDisplayed(true);
9219             
9220             // we wander
9221             var z = 0;
9222             var dom = this.dom;
9223             while (dom && dom.style) {
9224                 if (!isNaN(parseInt(dom.style.zIndex))) {
9225                     z = Math.max(z, parseInt(dom.style.zIndex));
9226                 }
9227                 dom = dom.parentNode;
9228             }
9229             // if we are masking the body - then it hides everything..
9230             if (this.dom == document.body) {
9231                 z = 1000000;
9232                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9233                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9234             }
9235            
9236             if(typeof msg == 'string'){
9237                 if(!this._maskMsg){
9238                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9239                         cls: "roo-el-mask-msg", 
9240                         cn: [
9241                             {
9242                                 tag: 'i',
9243                                 cls: 'fa fa-spinner fa-spin'
9244                             },
9245                             {
9246                                 tag: 'div'
9247                             }   
9248                         ]
9249                     }, true);
9250                 }
9251                 var mm = this._maskMsg;
9252                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9253                 if (mm.dom.lastChild) { // weird IE issue?
9254                     mm.dom.lastChild.innerHTML = msg;
9255                 }
9256                 mm.setDisplayed(true);
9257                 mm.center(this);
9258                 mm.setStyle('z-index', z + 102);
9259             }
9260             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9261                 this._mask.setHeight(this.getHeight());
9262             }
9263             this._mask.setStyle('z-index', z + 100);
9264             
9265             return this._mask;
9266         },
9267
9268         /**
9269          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9270          * it is cached for reuse.
9271          */
9272         unmask : function(removeEl){
9273             if(this._mask){
9274                 if(removeEl === true){
9275                     this._mask.remove();
9276                     delete this._mask;
9277                     if(this._maskMsg){
9278                         this._maskMsg.remove();
9279                         delete this._maskMsg;
9280                     }
9281                 }else{
9282                     this._mask.setDisplayed(false);
9283                     if(this._maskMsg){
9284                         this._maskMsg.setDisplayed(false);
9285                     }
9286                 }
9287             }
9288             this.removeClass("x-masked");
9289         },
9290
9291         /**
9292          * Returns true if this element is masked
9293          * @return {Boolean}
9294          */
9295         isMasked : function(){
9296             return this._mask && this._mask.isVisible();
9297         },
9298
9299         /**
9300          * Creates an iframe shim for this element to keep selects and other windowed objects from
9301          * showing through.
9302          * @return {Roo.Element} The new shim element
9303          */
9304         createShim : function(){
9305             var el = document.createElement('iframe');
9306             el.frameBorder = 'no';
9307             el.className = 'roo-shim';
9308             if(Roo.isIE && Roo.isSecure){
9309                 el.src = Roo.SSL_SECURE_URL;
9310             }
9311             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9312             shim.autoBoxAdjust = false;
9313             return shim;
9314         },
9315
9316         /**
9317          * Removes this element from the DOM and deletes it from the cache
9318          */
9319         remove : function(){
9320             if(this.dom.parentNode){
9321                 this.dom.parentNode.removeChild(this.dom);
9322             }
9323             delete El.cache[this.dom.id];
9324         },
9325
9326         /**
9327          * Sets up event handlers to add and remove a css class when the mouse is over this element
9328          * @param {String} className
9329          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9330          * mouseout events for children elements
9331          * @return {Roo.Element} this
9332          */
9333         addClassOnOver : function(className, preventFlicker){
9334             this.on("mouseover", function(){
9335                 Roo.fly(this, '_internal').addClass(className);
9336             }, this.dom);
9337             var removeFn = function(e){
9338                 if(preventFlicker !== true || !e.within(this, true)){
9339                     Roo.fly(this, '_internal').removeClass(className);
9340                 }
9341             };
9342             this.on("mouseout", removeFn, this.dom);
9343             return this;
9344         },
9345
9346         /**
9347          * Sets up event handlers to add and remove a css class when this element has the focus
9348          * @param {String} className
9349          * @return {Roo.Element} this
9350          */
9351         addClassOnFocus : function(className){
9352             this.on("focus", function(){
9353                 Roo.fly(this, '_internal').addClass(className);
9354             }, this.dom);
9355             this.on("blur", function(){
9356                 Roo.fly(this, '_internal').removeClass(className);
9357             }, this.dom);
9358             return this;
9359         },
9360         /**
9361          * 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)
9362          * @param {String} className
9363          * @return {Roo.Element} this
9364          */
9365         addClassOnClick : function(className){
9366             var dom = this.dom;
9367             this.on("mousedown", function(){
9368                 Roo.fly(dom, '_internal').addClass(className);
9369                 var d = Roo.get(document);
9370                 var fn = function(){
9371                     Roo.fly(dom, '_internal').removeClass(className);
9372                     d.removeListener("mouseup", fn);
9373                 };
9374                 d.on("mouseup", fn);
9375             });
9376             return this;
9377         },
9378
9379         /**
9380          * Stops the specified event from bubbling and optionally prevents the default action
9381          * @param {String} eventName
9382          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9383          * @return {Roo.Element} this
9384          */
9385         swallowEvent : function(eventName, preventDefault){
9386             var fn = function(e){
9387                 e.stopPropagation();
9388                 if(preventDefault){
9389                     e.preventDefault();
9390                 }
9391             };
9392             if(eventName instanceof Array){
9393                 for(var i = 0, len = eventName.length; i < len; i++){
9394                      this.on(eventName[i], fn);
9395                 }
9396                 return this;
9397             }
9398             this.on(eventName, fn);
9399             return this;
9400         },
9401
9402         /**
9403          * @private
9404          */
9405         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9406
9407         /**
9408          * Sizes this element to its parent element's dimensions performing
9409          * neccessary box adjustments.
9410          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9411          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9412          * @return {Roo.Element} this
9413          */
9414         fitToParent : function(monitorResize, targetParent) {
9415           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9416           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9417           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9418             return;
9419           }
9420           var p = Roo.get(targetParent || this.dom.parentNode);
9421           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9422           if (monitorResize === true) {
9423             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9424             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9425           }
9426           return this;
9427         },
9428
9429         /**
9430          * Gets the next sibling, skipping text nodes
9431          * @return {HTMLElement} The next sibling or null
9432          */
9433         getNextSibling : function(){
9434             var n = this.dom.nextSibling;
9435             while(n && n.nodeType != 1){
9436                 n = n.nextSibling;
9437             }
9438             return n;
9439         },
9440
9441         /**
9442          * Gets the previous sibling, skipping text nodes
9443          * @return {HTMLElement} The previous sibling or null
9444          */
9445         getPrevSibling : function(){
9446             var n = this.dom.previousSibling;
9447             while(n && n.nodeType != 1){
9448                 n = n.previousSibling;
9449             }
9450             return n;
9451         },
9452
9453
9454         /**
9455          * Appends the passed element(s) to this element
9456          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9457          * @return {Roo.Element} this
9458          */
9459         appendChild: function(el){
9460             el = Roo.get(el);
9461             el.appendTo(this);
9462             return this;
9463         },
9464
9465         /**
9466          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9467          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9468          * automatically generated with the specified attributes.
9469          * @param {HTMLElement} insertBefore (optional) a child element of this element
9470          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9471          * @return {Roo.Element} The new child element
9472          */
9473         createChild: function(config, insertBefore, returnDom){
9474             config = config || {tag:'div'};
9475             if(insertBefore){
9476                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9477             }
9478             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9479         },
9480
9481         /**
9482          * Appends this element to the passed element
9483          * @param {String/HTMLElement/Element} el The new parent element
9484          * @return {Roo.Element} this
9485          */
9486         appendTo: function(el){
9487             el = Roo.getDom(el);
9488             el.appendChild(this.dom);
9489             return this;
9490         },
9491
9492         /**
9493          * Inserts this element before the passed element in the DOM
9494          * @param {String/HTMLElement/Element} el The element to insert before
9495          * @return {Roo.Element} this
9496          */
9497         insertBefore: function(el){
9498             el = Roo.getDom(el);
9499             el.parentNode.insertBefore(this.dom, el);
9500             return this;
9501         },
9502
9503         /**
9504          * Inserts this element after the passed element in the DOM
9505          * @param {String/HTMLElement/Element} el The element to insert after
9506          * @return {Roo.Element} this
9507          */
9508         insertAfter: function(el){
9509             el = Roo.getDom(el);
9510             el.parentNode.insertBefore(this.dom, el.nextSibling);
9511             return this;
9512         },
9513
9514         /**
9515          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9516          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9517          * @return {Roo.Element} The new child
9518          */
9519         insertFirst: function(el, returnDom){
9520             el = el || {};
9521             if(typeof el == 'object' && !el.nodeType){ // dh config
9522                 return this.createChild(el, this.dom.firstChild, returnDom);
9523             }else{
9524                 el = Roo.getDom(el);
9525                 this.dom.insertBefore(el, this.dom.firstChild);
9526                 return !returnDom ? Roo.get(el) : el;
9527             }
9528         },
9529
9530         /**
9531          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9532          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9533          * @param {String} where (optional) 'before' or 'after' defaults to before
9534          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9535          * @return {Roo.Element} the inserted Element
9536          */
9537         insertSibling: function(el, where, returnDom){
9538             where = where ? where.toLowerCase() : 'before';
9539             el = el || {};
9540             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9541
9542             if(typeof el == 'object' && !el.nodeType){ // dh config
9543                 if(where == 'after' && !this.dom.nextSibling){
9544                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9545                 }else{
9546                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9547                 }
9548
9549             }else{
9550                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9551                             where == 'before' ? this.dom : this.dom.nextSibling);
9552                 if(!returnDom){
9553                     rt = Roo.get(rt);
9554                 }
9555             }
9556             return rt;
9557         },
9558
9559         /**
9560          * Creates and wraps this element with another element
9561          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9562          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9563          * @return {HTMLElement/Element} The newly created wrapper element
9564          */
9565         wrap: function(config, returnDom){
9566             if(!config){
9567                 config = {tag: "div"};
9568             }
9569             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9570             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9571             return newEl;
9572         },
9573
9574         /**
9575          * Replaces the passed element with this element
9576          * @param {String/HTMLElement/Element} el The element to replace
9577          * @return {Roo.Element} this
9578          */
9579         replace: function(el){
9580             el = Roo.get(el);
9581             this.insertBefore(el);
9582             el.remove();
9583             return this;
9584         },
9585
9586         /**
9587          * Inserts an html fragment into this element
9588          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9589          * @param {String} html The HTML fragment
9590          * @param {Boolean} returnEl True to return an Roo.Element
9591          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9592          */
9593         insertHtml : function(where, html, returnEl){
9594             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9595             return returnEl ? Roo.get(el) : el;
9596         },
9597
9598         /**
9599          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9600          * @param {Object} o The object with the attributes
9601          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9602          * @return {Roo.Element} this
9603          */
9604         set : function(o, useSet){
9605             var el = this.dom;
9606             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9607             for(var attr in o){
9608                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9609                 if(attr=="cls"){
9610                     el.className = o["cls"];
9611                 }else{
9612                     if(useSet) {
9613                         el.setAttribute(attr, o[attr]);
9614                     } else {
9615                         el[attr] = o[attr];
9616                     }
9617                 }
9618             }
9619             if(o.style){
9620                 Roo.DomHelper.applyStyles(el, o.style);
9621             }
9622             return this;
9623         },
9624
9625         /**
9626          * Convenience method for constructing a KeyMap
9627          * @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:
9628          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9629          * @param {Function} fn The function to call
9630          * @param {Object} scope (optional) The scope of the function
9631          * @return {Roo.KeyMap} The KeyMap created
9632          */
9633         addKeyListener : function(key, fn, scope){
9634             var config;
9635             if(typeof key != "object" || key instanceof Array){
9636                 config = {
9637                     key: key,
9638                     fn: fn,
9639                     scope: scope
9640                 };
9641             }else{
9642                 config = {
9643                     key : key.key,
9644                     shift : key.shift,
9645                     ctrl : key.ctrl,
9646                     alt : key.alt,
9647                     fn: fn,
9648                     scope: scope
9649                 };
9650             }
9651             return new Roo.KeyMap(this, config);
9652         },
9653
9654         /**
9655          * Creates a KeyMap for this element
9656          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9657          * @return {Roo.KeyMap} The KeyMap created
9658          */
9659         addKeyMap : function(config){
9660             return new Roo.KeyMap(this, config);
9661         },
9662
9663         /**
9664          * Returns true if this element is scrollable.
9665          * @return {Boolean}
9666          */
9667          isScrollable : function(){
9668             var dom = this.dom;
9669             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9670         },
9671
9672         /**
9673          * 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().
9674          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9675          * @param {Number} value The new scroll value
9676          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9677          * @return {Element} this
9678          */
9679
9680         scrollTo : function(side, value, animate){
9681             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9682             if(!animate || !A){
9683                 this.dom[prop] = value;
9684             }else{
9685                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9686                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9687             }
9688             return this;
9689         },
9690
9691         /**
9692          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9693          * within this element's scrollable range.
9694          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9695          * @param {Number} distance How far to scroll the element in pixels
9696          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9697          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9698          * was scrolled as far as it could go.
9699          */
9700          scroll : function(direction, distance, animate){
9701              if(!this.isScrollable()){
9702                  return;
9703              }
9704              var el = this.dom;
9705              var l = el.scrollLeft, t = el.scrollTop;
9706              var w = el.scrollWidth, h = el.scrollHeight;
9707              var cw = el.clientWidth, ch = el.clientHeight;
9708              direction = direction.toLowerCase();
9709              var scrolled = false;
9710              var a = this.preanim(arguments, 2);
9711              switch(direction){
9712                  case "l":
9713                  case "left":
9714                      if(w - l > cw){
9715                          var v = Math.min(l + distance, w-cw);
9716                          this.scrollTo("left", v, a);
9717                          scrolled = true;
9718                      }
9719                      break;
9720                 case "r":
9721                 case "right":
9722                      if(l > 0){
9723                          var v = Math.max(l - distance, 0);
9724                          this.scrollTo("left", v, a);
9725                          scrolled = true;
9726                      }
9727                      break;
9728                 case "t":
9729                 case "top":
9730                 case "up":
9731                      if(t > 0){
9732                          var v = Math.max(t - distance, 0);
9733                          this.scrollTo("top", v, a);
9734                          scrolled = true;
9735                      }
9736                      break;
9737                 case "b":
9738                 case "bottom":
9739                 case "down":
9740                      if(h - t > ch){
9741                          var v = Math.min(t + distance, h-ch);
9742                          this.scrollTo("top", v, a);
9743                          scrolled = true;
9744                      }
9745                      break;
9746              }
9747              return scrolled;
9748         },
9749
9750         /**
9751          * Translates the passed page coordinates into left/top css values for this element
9752          * @param {Number/Array} x The page x or an array containing [x, y]
9753          * @param {Number} y The page y
9754          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9755          */
9756         translatePoints : function(x, y){
9757             if(typeof x == 'object' || x instanceof Array){
9758                 y = x[1]; x = x[0];
9759             }
9760             var p = this.getStyle('position');
9761             var o = this.getXY();
9762
9763             var l = parseInt(this.getStyle('left'), 10);
9764             var t = parseInt(this.getStyle('top'), 10);
9765
9766             if(isNaN(l)){
9767                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9768             }
9769             if(isNaN(t)){
9770                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9771             }
9772
9773             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9774         },
9775
9776         /**
9777          * Returns the current scroll position of the element.
9778          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9779          */
9780         getScroll : function(){
9781             var d = this.dom, doc = document;
9782             if(d == doc || d == doc.body){
9783                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9784                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9785                 return {left: l, top: t};
9786             }else{
9787                 return {left: d.scrollLeft, top: d.scrollTop};
9788             }
9789         },
9790
9791         /**
9792          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9793          * are convert to standard 6 digit hex color.
9794          * @param {String} attr The css attribute
9795          * @param {String} defaultValue The default value to use when a valid color isn't found
9796          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9797          * YUI color anims.
9798          */
9799         getColor : function(attr, defaultValue, prefix){
9800             var v = this.getStyle(attr);
9801             if(!v || v == "transparent" || v == "inherit") {
9802                 return defaultValue;
9803             }
9804             var color = typeof prefix == "undefined" ? "#" : prefix;
9805             if(v.substr(0, 4) == "rgb("){
9806                 var rvs = v.slice(4, v.length -1).split(",");
9807                 for(var i = 0; i < 3; i++){
9808                     var h = parseInt(rvs[i]).toString(16);
9809                     if(h < 16){
9810                         h = "0" + h;
9811                     }
9812                     color += h;
9813                 }
9814             } else {
9815                 if(v.substr(0, 1) == "#"){
9816                     if(v.length == 4) {
9817                         for(var i = 1; i < 4; i++){
9818                             var c = v.charAt(i);
9819                             color +=  c + c;
9820                         }
9821                     }else if(v.length == 7){
9822                         color += v.substr(1);
9823                     }
9824                 }
9825             }
9826             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9827         },
9828
9829         /**
9830          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9831          * gradient background, rounded corners and a 4-way shadow.
9832          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9833          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9834          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9835          * @return {Roo.Element} this
9836          */
9837         boxWrap : function(cls){
9838             cls = cls || 'x-box';
9839             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9840             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9841             return el;
9842         },
9843
9844         /**
9845          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9846          * @param {String} namespace The namespace in which to look for the attribute
9847          * @param {String} name The attribute name
9848          * @return {String} The attribute value
9849          */
9850         getAttributeNS : Roo.isIE ? function(ns, name){
9851             var d = this.dom;
9852             var type = typeof d[ns+":"+name];
9853             if(type != 'undefined' && type != 'unknown'){
9854                 return d[ns+":"+name];
9855             }
9856             return d[name];
9857         } : function(ns, name){
9858             var d = this.dom;
9859             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9860         },
9861         
9862         
9863         /**
9864          * Sets or Returns the value the dom attribute value
9865          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9866          * @param {String} value (optional) The value to set the attribute to
9867          * @return {String} The attribute value
9868          */
9869         attr : function(name){
9870             if (arguments.length > 1) {
9871                 this.dom.setAttribute(name, arguments[1]);
9872                 return arguments[1];
9873             }
9874             if (typeof(name) == 'object') {
9875                 for(var i in name) {
9876                     this.attr(i, name[i]);
9877                 }
9878                 return name;
9879             }
9880             
9881             
9882             if (!this.dom.hasAttribute(name)) {
9883                 return undefined;
9884             }
9885             return this.dom.getAttribute(name);
9886         }
9887         
9888         
9889         
9890     };
9891
9892     var ep = El.prototype;
9893
9894     /**
9895      * Appends an event handler (Shorthand for addListener)
9896      * @param {String}   eventName     The type of event to append
9897      * @param {Function} fn        The method the event invokes
9898      * @param {Object} scope       (optional) The scope (this object) of the fn
9899      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9900      * @method
9901      */
9902     ep.on = ep.addListener;
9903         // backwards compat
9904     ep.mon = ep.addListener;
9905
9906     /**
9907      * Removes an event handler from this element (shorthand for removeListener)
9908      * @param {String} eventName the type of event to remove
9909      * @param {Function} fn the method the event invokes
9910      * @return {Roo.Element} this
9911      * @method
9912      */
9913     ep.un = ep.removeListener;
9914
9915     /**
9916      * true to automatically adjust width and height settings for box-model issues (default to true)
9917      */
9918     ep.autoBoxAdjust = true;
9919
9920     // private
9921     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9922
9923     // private
9924     El.addUnits = function(v, defaultUnit){
9925         if(v === "" || v == "auto"){
9926             return v;
9927         }
9928         if(v === undefined){
9929             return '';
9930         }
9931         if(typeof v == "number" || !El.unitPattern.test(v)){
9932             return v + (defaultUnit || 'px');
9933         }
9934         return v;
9935     };
9936
9937     // special markup used throughout Roo when box wrapping elements
9938     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>';
9939     /**
9940      * Visibility mode constant - Use visibility to hide element
9941      * @static
9942      * @type Number
9943      */
9944     El.VISIBILITY = 1;
9945     /**
9946      * Visibility mode constant - Use display to hide element
9947      * @static
9948      * @type Number
9949      */
9950     El.DISPLAY = 2;
9951
9952     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9953     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9954     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9955
9956
9957
9958     /**
9959      * @private
9960      */
9961     El.cache = {};
9962
9963     var docEl;
9964
9965     /**
9966      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9967      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9968      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9969      * @return {Element} The Element object
9970      * @static
9971      */
9972     El.get = function(el){
9973         var ex, elm, id;
9974         if(!el){ return null; }
9975         if(typeof el == "string"){ // element id
9976             if(!(elm = document.getElementById(el))){
9977                 return null;
9978             }
9979             if(ex = El.cache[el]){
9980                 ex.dom = elm;
9981             }else{
9982                 ex = El.cache[el] = new El(elm);
9983             }
9984             return ex;
9985         }else if(el.tagName){ // dom element
9986             if(!(id = el.id)){
9987                 id = Roo.id(el);
9988             }
9989             if(ex = El.cache[id]){
9990                 ex.dom = el;
9991             }else{
9992                 ex = El.cache[id] = new El(el);
9993             }
9994             return ex;
9995         }else if(el instanceof El){
9996             if(el != docEl){
9997                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9998                                                               // catch case where it hasn't been appended
9999                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10000             }
10001             return el;
10002         }else if(el.isComposite){
10003             return el;
10004         }else if(el instanceof Array){
10005             return El.select(el);
10006         }else if(el == document){
10007             // create a bogus element object representing the document object
10008             if(!docEl){
10009                 var f = function(){};
10010                 f.prototype = El.prototype;
10011                 docEl = new f();
10012                 docEl.dom = document;
10013             }
10014             return docEl;
10015         }
10016         return null;
10017     };
10018
10019     // private
10020     El.uncache = function(el){
10021         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10022             if(a[i]){
10023                 delete El.cache[a[i].id || a[i]];
10024             }
10025         }
10026     };
10027
10028     // private
10029     // Garbage collection - uncache elements/purge listeners on orphaned elements
10030     // so we don't hold a reference and cause the browser to retain them
10031     El.garbageCollect = function(){
10032         if(!Roo.enableGarbageCollector){
10033             clearInterval(El.collectorThread);
10034             return;
10035         }
10036         for(var eid in El.cache){
10037             var el = El.cache[eid], d = el.dom;
10038             // -------------------------------------------------------
10039             // Determining what is garbage:
10040             // -------------------------------------------------------
10041             // !d
10042             // dom node is null, definitely garbage
10043             // -------------------------------------------------------
10044             // !d.parentNode
10045             // no parentNode == direct orphan, definitely garbage
10046             // -------------------------------------------------------
10047             // !d.offsetParent && !document.getElementById(eid)
10048             // display none elements have no offsetParent so we will
10049             // also try to look it up by it's id. However, check
10050             // offsetParent first so we don't do unneeded lookups.
10051             // This enables collection of elements that are not orphans
10052             // directly, but somewhere up the line they have an orphan
10053             // parent.
10054             // -------------------------------------------------------
10055             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10056                 delete El.cache[eid];
10057                 if(d && Roo.enableListenerCollection){
10058                     E.purgeElement(d);
10059                 }
10060             }
10061         }
10062     }
10063     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10064
10065
10066     // dom is optional
10067     El.Flyweight = function(dom){
10068         this.dom = dom;
10069     };
10070     El.Flyweight.prototype = El.prototype;
10071
10072     El._flyweights = {};
10073     /**
10074      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10075      * the dom node can be overwritten by other code.
10076      * @param {String/HTMLElement} el The dom node or id
10077      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10078      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10079      * @static
10080      * @return {Element} The shared Element object
10081      */
10082     El.fly = function(el, named){
10083         named = named || '_global';
10084         el = Roo.getDom(el);
10085         if(!el){
10086             return null;
10087         }
10088         if(!El._flyweights[named]){
10089             El._flyweights[named] = new El.Flyweight();
10090         }
10091         El._flyweights[named].dom = el;
10092         return El._flyweights[named];
10093     };
10094
10095     /**
10096      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10097      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10098      * Shorthand of {@link Roo.Element#get}
10099      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10100      * @return {Element} The Element object
10101      * @member Roo
10102      * @method get
10103      */
10104     Roo.get = El.get;
10105     /**
10106      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10107      * the dom node can be overwritten by other code.
10108      * Shorthand of {@link Roo.Element#fly}
10109      * @param {String/HTMLElement} el The dom node or id
10110      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10111      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10112      * @static
10113      * @return {Element} The shared Element object
10114      * @member Roo
10115      * @method fly
10116      */
10117     Roo.fly = El.fly;
10118
10119     // speedy lookup for elements never to box adjust
10120     var noBoxAdjust = Roo.isStrict ? {
10121         select:1
10122     } : {
10123         input:1, select:1, textarea:1
10124     };
10125     if(Roo.isIE || Roo.isGecko){
10126         noBoxAdjust['button'] = 1;
10127     }
10128
10129
10130     Roo.EventManager.on(window, 'unload', function(){
10131         delete El.cache;
10132         delete El._flyweights;
10133     });
10134 })();
10135
10136
10137
10138
10139 if(Roo.DomQuery){
10140     Roo.Element.selectorFunction = Roo.DomQuery.select;
10141 }
10142
10143 Roo.Element.select = function(selector, unique, root){
10144     var els;
10145     if(typeof selector == "string"){
10146         els = Roo.Element.selectorFunction(selector, root);
10147     }else if(selector.length !== undefined){
10148         els = selector;
10149     }else{
10150         throw "Invalid selector";
10151     }
10152     if(unique === true){
10153         return new Roo.CompositeElement(els);
10154     }else{
10155         return new Roo.CompositeElementLite(els);
10156     }
10157 };
10158 /**
10159  * Selects elements based on the passed CSS selector to enable working on them as 1.
10160  * @param {String/Array} selector The CSS selector or an array of elements
10161  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10162  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10163  * @return {CompositeElementLite/CompositeElement}
10164  * @member Roo
10165  * @method select
10166  */
10167 Roo.select = Roo.Element.select;
10168
10169
10170
10171
10172
10173
10174
10175
10176
10177
10178
10179
10180
10181
10182 /*
10183  * Based on:
10184  * Ext JS Library 1.1.1
10185  * Copyright(c) 2006-2007, Ext JS, LLC.
10186  *
10187  * Originally Released Under LGPL - original licence link has changed is not relivant.
10188  *
10189  * Fork - LGPL
10190  * <script type="text/javascript">
10191  */
10192
10193
10194
10195 //Notifies Element that fx methods are available
10196 Roo.enableFx = true;
10197
10198 /**
10199  * @class Roo.Fx
10200  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10201  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10202  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10203  * Element effects to work.</p><br/>
10204  *
10205  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10206  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10207  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10208  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10209  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10210  * expected results and should be done with care.</p><br/>
10211  *
10212  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10213  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10214 <pre>
10215 Value  Description
10216 -----  -----------------------------
10217 tl     The top left corner
10218 t      The center of the top edge
10219 tr     The top right corner
10220 l      The center of the left edge
10221 r      The center of the right edge
10222 bl     The bottom left corner
10223 b      The center of the bottom edge
10224 br     The bottom right corner
10225 </pre>
10226  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10227  * below are common options that can be passed to any Fx method.</b>
10228  * @cfg {Function} callback A function called when the effect is finished
10229  * @cfg {Object} scope The scope of the effect function
10230  * @cfg {String} easing A valid Easing value for the effect
10231  * @cfg {String} afterCls A css class to apply after the effect
10232  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10233  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10234  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10235  * effects that end with the element being visually hidden, ignored otherwise)
10236  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10237  * a function which returns such a specification that will be applied to the Element after the effect finishes
10238  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10239  * @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
10240  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10241  */
10242 Roo.Fx = {
10243         /**
10244          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10245          * origin for the slide effect.  This function automatically handles wrapping the element with
10246          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10247          * Usage:
10248          *<pre><code>
10249 // default: slide the element in from the top
10250 el.slideIn();
10251
10252 // custom: slide the element in from the right with a 2-second duration
10253 el.slideIn('r', { duration: 2 });
10254
10255 // common config options shown with default values
10256 el.slideIn('t', {
10257     easing: 'easeOut',
10258     duration: .5
10259 });
10260 </code></pre>
10261          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10262          * @param {Object} options (optional) Object literal with any of the Fx config options
10263          * @return {Roo.Element} The Element
10264          */
10265     slideIn : function(anchor, o){
10266         var el = this.getFxEl();
10267         o = o || {};
10268
10269         el.queueFx(o, function(){
10270
10271             anchor = anchor || "t";
10272
10273             // fix display to visibility
10274             this.fixDisplay();
10275
10276             // restore values after effect
10277             var r = this.getFxRestore();
10278             var b = this.getBox();
10279             // fixed size for slide
10280             this.setSize(b);
10281
10282             // wrap if needed
10283             var wrap = this.fxWrap(r.pos, o, "hidden");
10284
10285             var st = this.dom.style;
10286             st.visibility = "visible";
10287             st.position = "absolute";
10288
10289             // clear out temp styles after slide and unwrap
10290             var after = function(){
10291                 el.fxUnwrap(wrap, r.pos, o);
10292                 st.width = r.width;
10293                 st.height = r.height;
10294                 el.afterFx(o);
10295             };
10296             // time to calc the positions
10297             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10298
10299             switch(anchor.toLowerCase()){
10300                 case "t":
10301                     wrap.setSize(b.width, 0);
10302                     st.left = st.bottom = "0";
10303                     a = {height: bh};
10304                 break;
10305                 case "l":
10306                     wrap.setSize(0, b.height);
10307                     st.right = st.top = "0";
10308                     a = {width: bw};
10309                 break;
10310                 case "r":
10311                     wrap.setSize(0, b.height);
10312                     wrap.setX(b.right);
10313                     st.left = st.top = "0";
10314                     a = {width: bw, points: pt};
10315                 break;
10316                 case "b":
10317                     wrap.setSize(b.width, 0);
10318                     wrap.setY(b.bottom);
10319                     st.left = st.top = "0";
10320                     a = {height: bh, points: pt};
10321                 break;
10322                 case "tl":
10323                     wrap.setSize(0, 0);
10324                     st.right = st.bottom = "0";
10325                     a = {width: bw, height: bh};
10326                 break;
10327                 case "bl":
10328                     wrap.setSize(0, 0);
10329                     wrap.setY(b.y+b.height);
10330                     st.right = st.top = "0";
10331                     a = {width: bw, height: bh, points: pt};
10332                 break;
10333                 case "br":
10334                     wrap.setSize(0, 0);
10335                     wrap.setXY([b.right, b.bottom]);
10336                     st.left = st.top = "0";
10337                     a = {width: bw, height: bh, points: pt};
10338                 break;
10339                 case "tr":
10340                     wrap.setSize(0, 0);
10341                     wrap.setX(b.x+b.width);
10342                     st.left = st.bottom = "0";
10343                     a = {width: bw, height: bh, points: pt};
10344                 break;
10345             }
10346             this.dom.style.visibility = "visible";
10347             wrap.show();
10348
10349             arguments.callee.anim = wrap.fxanim(a,
10350                 o,
10351                 'motion',
10352                 .5,
10353                 'easeOut', after);
10354         });
10355         return this;
10356     },
10357     
10358         /**
10359          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10360          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10361          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10362          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10363          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10364          * Usage:
10365          *<pre><code>
10366 // default: slide the element out to the top
10367 el.slideOut();
10368
10369 // custom: slide the element out to the right with a 2-second duration
10370 el.slideOut('r', { duration: 2 });
10371
10372 // common config options shown with default values
10373 el.slideOut('t', {
10374     easing: 'easeOut',
10375     duration: .5,
10376     remove: false,
10377     useDisplay: false
10378 });
10379 </code></pre>
10380          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10381          * @param {Object} options (optional) Object literal with any of the Fx config options
10382          * @return {Roo.Element} The Element
10383          */
10384     slideOut : function(anchor, o){
10385         var el = this.getFxEl();
10386         o = o || {};
10387
10388         el.queueFx(o, function(){
10389
10390             anchor = anchor || "t";
10391
10392             // restore values after effect
10393             var r = this.getFxRestore();
10394             
10395             var b = this.getBox();
10396             // fixed size for slide
10397             this.setSize(b);
10398
10399             // wrap if needed
10400             var wrap = this.fxWrap(r.pos, o, "visible");
10401
10402             var st = this.dom.style;
10403             st.visibility = "visible";
10404             st.position = "absolute";
10405
10406             wrap.setSize(b);
10407
10408             var after = function(){
10409                 if(o.useDisplay){
10410                     el.setDisplayed(false);
10411                 }else{
10412                     el.hide();
10413                 }
10414
10415                 el.fxUnwrap(wrap, r.pos, o);
10416
10417                 st.width = r.width;
10418                 st.height = r.height;
10419
10420                 el.afterFx(o);
10421             };
10422
10423             var a, zero = {to: 0};
10424             switch(anchor.toLowerCase()){
10425                 case "t":
10426                     st.left = st.bottom = "0";
10427                     a = {height: zero};
10428                 break;
10429                 case "l":
10430                     st.right = st.top = "0";
10431                     a = {width: zero};
10432                 break;
10433                 case "r":
10434                     st.left = st.top = "0";
10435                     a = {width: zero, points: {to:[b.right, b.y]}};
10436                 break;
10437                 case "b":
10438                     st.left = st.top = "0";
10439                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10440                 break;
10441                 case "tl":
10442                     st.right = st.bottom = "0";
10443                     a = {width: zero, height: zero};
10444                 break;
10445                 case "bl":
10446                     st.right = st.top = "0";
10447                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10448                 break;
10449                 case "br":
10450                     st.left = st.top = "0";
10451                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10452                 break;
10453                 case "tr":
10454                     st.left = st.bottom = "0";
10455                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10456                 break;
10457             }
10458
10459             arguments.callee.anim = wrap.fxanim(a,
10460                 o,
10461                 'motion',
10462                 .5,
10463                 "easeOut", after);
10464         });
10465         return this;
10466     },
10467
10468         /**
10469          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10470          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10471          * The element must be removed from the DOM using the 'remove' config option if desired.
10472          * Usage:
10473          *<pre><code>
10474 // default
10475 el.puff();
10476
10477 // common config options shown with default values
10478 el.puff({
10479     easing: 'easeOut',
10480     duration: .5,
10481     remove: false,
10482     useDisplay: false
10483 });
10484 </code></pre>
10485          * @param {Object} options (optional) Object literal with any of the Fx config options
10486          * @return {Roo.Element} The Element
10487          */
10488     puff : function(o){
10489         var el = this.getFxEl();
10490         o = o || {};
10491
10492         el.queueFx(o, function(){
10493             this.clearOpacity();
10494             this.show();
10495
10496             // restore values after effect
10497             var r = this.getFxRestore();
10498             var st = this.dom.style;
10499
10500             var after = function(){
10501                 if(o.useDisplay){
10502                     el.setDisplayed(false);
10503                 }else{
10504                     el.hide();
10505                 }
10506
10507                 el.clearOpacity();
10508
10509                 el.setPositioning(r.pos);
10510                 st.width = r.width;
10511                 st.height = r.height;
10512                 st.fontSize = '';
10513                 el.afterFx(o);
10514             };
10515
10516             var width = this.getWidth();
10517             var height = this.getHeight();
10518
10519             arguments.callee.anim = this.fxanim({
10520                     width : {to: this.adjustWidth(width * 2)},
10521                     height : {to: this.adjustHeight(height * 2)},
10522                     points : {by: [-(width * .5), -(height * .5)]},
10523                     opacity : {to: 0},
10524                     fontSize: {to:200, unit: "%"}
10525                 },
10526                 o,
10527                 'motion',
10528                 .5,
10529                 "easeOut", after);
10530         });
10531         return this;
10532     },
10533
10534         /**
10535          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10536          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10537          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10538          * Usage:
10539          *<pre><code>
10540 // default
10541 el.switchOff();
10542
10543 // all config options shown with default values
10544 el.switchOff({
10545     easing: 'easeIn',
10546     duration: .3,
10547     remove: false,
10548     useDisplay: false
10549 });
10550 </code></pre>
10551          * @param {Object} options (optional) Object literal with any of the Fx config options
10552          * @return {Roo.Element} The Element
10553          */
10554     switchOff : function(o){
10555         var el = this.getFxEl();
10556         o = o || {};
10557
10558         el.queueFx(o, function(){
10559             this.clearOpacity();
10560             this.clip();
10561
10562             // restore values after effect
10563             var r = this.getFxRestore();
10564             var st = this.dom.style;
10565
10566             var after = function(){
10567                 if(o.useDisplay){
10568                     el.setDisplayed(false);
10569                 }else{
10570                     el.hide();
10571                 }
10572
10573                 el.clearOpacity();
10574                 el.setPositioning(r.pos);
10575                 st.width = r.width;
10576                 st.height = r.height;
10577
10578                 el.afterFx(o);
10579             };
10580
10581             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10582                 this.clearOpacity();
10583                 (function(){
10584                     this.fxanim({
10585                         height:{to:1},
10586                         points:{by:[0, this.getHeight() * .5]}
10587                     }, o, 'motion', 0.3, 'easeIn', after);
10588                 }).defer(100, this);
10589             });
10590         });
10591         return this;
10592     },
10593
10594     /**
10595      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10596      * changed using the "attr" config option) and then fading back to the original color. If no original
10597      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10598      * Usage:
10599 <pre><code>
10600 // default: highlight background to yellow
10601 el.highlight();
10602
10603 // custom: highlight foreground text to blue for 2 seconds
10604 el.highlight("0000ff", { attr: 'color', duration: 2 });
10605
10606 // common config options shown with default values
10607 el.highlight("ffff9c", {
10608     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10609     endColor: (current color) or "ffffff",
10610     easing: 'easeIn',
10611     duration: 1
10612 });
10613 </code></pre>
10614      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10615      * @param {Object} options (optional) Object literal with any of the Fx config options
10616      * @return {Roo.Element} The Element
10617      */ 
10618     highlight : function(color, o){
10619         var el = this.getFxEl();
10620         o = o || {};
10621
10622         el.queueFx(o, function(){
10623             color = color || "ffff9c";
10624             attr = o.attr || "backgroundColor";
10625
10626             this.clearOpacity();
10627             this.show();
10628
10629             var origColor = this.getColor(attr);
10630             var restoreColor = this.dom.style[attr];
10631             endColor = (o.endColor || origColor) || "ffffff";
10632
10633             var after = function(){
10634                 el.dom.style[attr] = restoreColor;
10635                 el.afterFx(o);
10636             };
10637
10638             var a = {};
10639             a[attr] = {from: color, to: endColor};
10640             arguments.callee.anim = this.fxanim(a,
10641                 o,
10642                 'color',
10643                 1,
10644                 'easeIn', after);
10645         });
10646         return this;
10647     },
10648
10649    /**
10650     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10651     * Usage:
10652 <pre><code>
10653 // default: a single light blue ripple
10654 el.frame();
10655
10656 // custom: 3 red ripples lasting 3 seconds total
10657 el.frame("ff0000", 3, { duration: 3 });
10658
10659 // common config options shown with default values
10660 el.frame("C3DAF9", 1, {
10661     duration: 1 //duration of entire animation (not each individual ripple)
10662     // Note: Easing is not configurable and will be ignored if included
10663 });
10664 </code></pre>
10665     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10666     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10667     * @param {Object} options (optional) Object literal with any of the Fx config options
10668     * @return {Roo.Element} The Element
10669     */
10670     frame : function(color, count, o){
10671         var el = this.getFxEl();
10672         o = o || {};
10673
10674         el.queueFx(o, function(){
10675             color = color || "#C3DAF9";
10676             if(color.length == 6){
10677                 color = "#" + color;
10678             }
10679             count = count || 1;
10680             duration = o.duration || 1;
10681             this.show();
10682
10683             var b = this.getBox();
10684             var animFn = function(){
10685                 var proxy = this.createProxy({
10686
10687                      style:{
10688                         visbility:"hidden",
10689                         position:"absolute",
10690                         "z-index":"35000", // yee haw
10691                         border:"0px solid " + color
10692                      }
10693                   });
10694                 var scale = Roo.isBorderBox ? 2 : 1;
10695                 proxy.animate({
10696                     top:{from:b.y, to:b.y - 20},
10697                     left:{from:b.x, to:b.x - 20},
10698                     borderWidth:{from:0, to:10},
10699                     opacity:{from:1, to:0},
10700                     height:{from:b.height, to:(b.height + (20*scale))},
10701                     width:{from:b.width, to:(b.width + (20*scale))}
10702                 }, duration, function(){
10703                     proxy.remove();
10704                 });
10705                 if(--count > 0){
10706                      animFn.defer((duration/2)*1000, this);
10707                 }else{
10708                     el.afterFx(o);
10709                 }
10710             };
10711             animFn.call(this);
10712         });
10713         return this;
10714     },
10715
10716    /**
10717     * Creates a pause before any subsequent queued effects begin.  If there are
10718     * no effects queued after the pause it will have no effect.
10719     * Usage:
10720 <pre><code>
10721 el.pause(1);
10722 </code></pre>
10723     * @param {Number} seconds The length of time to pause (in seconds)
10724     * @return {Roo.Element} The Element
10725     */
10726     pause : function(seconds){
10727         var el = this.getFxEl();
10728         var o = {};
10729
10730         el.queueFx(o, function(){
10731             setTimeout(function(){
10732                 el.afterFx(o);
10733             }, seconds * 1000);
10734         });
10735         return this;
10736     },
10737
10738    /**
10739     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10740     * using the "endOpacity" config option.
10741     * Usage:
10742 <pre><code>
10743 // default: fade in from opacity 0 to 100%
10744 el.fadeIn();
10745
10746 // custom: fade in from opacity 0 to 75% over 2 seconds
10747 el.fadeIn({ endOpacity: .75, duration: 2});
10748
10749 // common config options shown with default values
10750 el.fadeIn({
10751     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10752     easing: 'easeOut',
10753     duration: .5
10754 });
10755 </code></pre>
10756     * @param {Object} options (optional) Object literal with any of the Fx config options
10757     * @return {Roo.Element} The Element
10758     */
10759     fadeIn : function(o){
10760         var el = this.getFxEl();
10761         o = o || {};
10762         el.queueFx(o, function(){
10763             this.setOpacity(0);
10764             this.fixDisplay();
10765             this.dom.style.visibility = 'visible';
10766             var to = o.endOpacity || 1;
10767             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10768                 o, null, .5, "easeOut", function(){
10769                 if(to == 1){
10770                     this.clearOpacity();
10771                 }
10772                 el.afterFx(o);
10773             });
10774         });
10775         return this;
10776     },
10777
10778    /**
10779     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10780     * using the "endOpacity" config option.
10781     * Usage:
10782 <pre><code>
10783 // default: fade out from the element's current opacity to 0
10784 el.fadeOut();
10785
10786 // custom: fade out from the element's current opacity to 25% over 2 seconds
10787 el.fadeOut({ endOpacity: .25, duration: 2});
10788
10789 // common config options shown with default values
10790 el.fadeOut({
10791     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10792     easing: 'easeOut',
10793     duration: .5
10794     remove: false,
10795     useDisplay: false
10796 });
10797 </code></pre>
10798     * @param {Object} options (optional) Object literal with any of the Fx config options
10799     * @return {Roo.Element} The Element
10800     */
10801     fadeOut : function(o){
10802         var el = this.getFxEl();
10803         o = o || {};
10804         el.queueFx(o, function(){
10805             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10806                 o, null, .5, "easeOut", function(){
10807                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10808                      this.dom.style.display = "none";
10809                 }else{
10810                      this.dom.style.visibility = "hidden";
10811                 }
10812                 this.clearOpacity();
10813                 el.afterFx(o);
10814             });
10815         });
10816         return this;
10817     },
10818
10819    /**
10820     * Animates the transition of an element's dimensions from a starting height/width
10821     * to an ending height/width.
10822     * Usage:
10823 <pre><code>
10824 // change height and width to 100x100 pixels
10825 el.scale(100, 100);
10826
10827 // common config options shown with default values.  The height and width will default to
10828 // the element's existing values if passed as null.
10829 el.scale(
10830     [element's width],
10831     [element's height], {
10832     easing: 'easeOut',
10833     duration: .35
10834 });
10835 </code></pre>
10836     * @param {Number} width  The new width (pass undefined to keep the original width)
10837     * @param {Number} height  The new height (pass undefined to keep the original height)
10838     * @param {Object} options (optional) Object literal with any of the Fx config options
10839     * @return {Roo.Element} The Element
10840     */
10841     scale : function(w, h, o){
10842         this.shift(Roo.apply({}, o, {
10843             width: w,
10844             height: h
10845         }));
10846         return this;
10847     },
10848
10849    /**
10850     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10851     * Any of these properties not specified in the config object will not be changed.  This effect 
10852     * requires that at least one new dimension, position or opacity setting must be passed in on
10853     * the config object in order for the function to have any effect.
10854     * Usage:
10855 <pre><code>
10856 // slide the element horizontally to x position 200 while changing the height and opacity
10857 el.shift({ x: 200, height: 50, opacity: .8 });
10858
10859 // common config options shown with default values.
10860 el.shift({
10861     width: [element's width],
10862     height: [element's height],
10863     x: [element's x position],
10864     y: [element's y position],
10865     opacity: [element's opacity],
10866     easing: 'easeOut',
10867     duration: .35
10868 });
10869 </code></pre>
10870     * @param {Object} options  Object literal with any of the Fx config options
10871     * @return {Roo.Element} The Element
10872     */
10873     shift : function(o){
10874         var el = this.getFxEl();
10875         o = o || {};
10876         el.queueFx(o, function(){
10877             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10878             if(w !== undefined){
10879                 a.width = {to: this.adjustWidth(w)};
10880             }
10881             if(h !== undefined){
10882                 a.height = {to: this.adjustHeight(h)};
10883             }
10884             if(x !== undefined || y !== undefined){
10885                 a.points = {to: [
10886                     x !== undefined ? x : this.getX(),
10887                     y !== undefined ? y : this.getY()
10888                 ]};
10889             }
10890             if(op !== undefined){
10891                 a.opacity = {to: op};
10892             }
10893             if(o.xy !== undefined){
10894                 a.points = {to: o.xy};
10895             }
10896             arguments.callee.anim = this.fxanim(a,
10897                 o, 'motion', .35, "easeOut", function(){
10898                 el.afterFx(o);
10899             });
10900         });
10901         return this;
10902     },
10903
10904         /**
10905          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10906          * ending point of the effect.
10907          * Usage:
10908          *<pre><code>
10909 // default: slide the element downward while fading out
10910 el.ghost();
10911
10912 // custom: slide the element out to the right with a 2-second duration
10913 el.ghost('r', { duration: 2 });
10914
10915 // common config options shown with default values
10916 el.ghost('b', {
10917     easing: 'easeOut',
10918     duration: .5
10919     remove: false,
10920     useDisplay: false
10921 });
10922 </code></pre>
10923          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10924          * @param {Object} options (optional) Object literal with any of the Fx config options
10925          * @return {Roo.Element} The Element
10926          */
10927     ghost : function(anchor, o){
10928         var el = this.getFxEl();
10929         o = o || {};
10930
10931         el.queueFx(o, function(){
10932             anchor = anchor || "b";
10933
10934             // restore values after effect
10935             var r = this.getFxRestore();
10936             var w = this.getWidth(),
10937                 h = this.getHeight();
10938
10939             var st = this.dom.style;
10940
10941             var after = function(){
10942                 if(o.useDisplay){
10943                     el.setDisplayed(false);
10944                 }else{
10945                     el.hide();
10946                 }
10947
10948                 el.clearOpacity();
10949                 el.setPositioning(r.pos);
10950                 st.width = r.width;
10951                 st.height = r.height;
10952
10953                 el.afterFx(o);
10954             };
10955
10956             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10957             switch(anchor.toLowerCase()){
10958                 case "t":
10959                     pt.by = [0, -h];
10960                 break;
10961                 case "l":
10962                     pt.by = [-w, 0];
10963                 break;
10964                 case "r":
10965                     pt.by = [w, 0];
10966                 break;
10967                 case "b":
10968                     pt.by = [0, h];
10969                 break;
10970                 case "tl":
10971                     pt.by = [-w, -h];
10972                 break;
10973                 case "bl":
10974                     pt.by = [-w, h];
10975                 break;
10976                 case "br":
10977                     pt.by = [w, h];
10978                 break;
10979                 case "tr":
10980                     pt.by = [w, -h];
10981                 break;
10982             }
10983
10984             arguments.callee.anim = this.fxanim(a,
10985                 o,
10986                 'motion',
10987                 .5,
10988                 "easeOut", after);
10989         });
10990         return this;
10991     },
10992
10993         /**
10994          * Ensures that all effects queued after syncFx is called on the element are
10995          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10996          * @return {Roo.Element} The Element
10997          */
10998     syncFx : function(){
10999         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11000             block : false,
11001             concurrent : true,
11002             stopFx : false
11003         });
11004         return this;
11005     },
11006
11007         /**
11008          * Ensures that all effects queued after sequenceFx is called on the element are
11009          * run in sequence.  This is the opposite of {@link #syncFx}.
11010          * @return {Roo.Element} The Element
11011          */
11012     sequenceFx : function(){
11013         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11014             block : false,
11015             concurrent : false,
11016             stopFx : false
11017         });
11018         return this;
11019     },
11020
11021         /* @private */
11022     nextFx : function(){
11023         var ef = this.fxQueue[0];
11024         if(ef){
11025             ef.call(this);
11026         }
11027     },
11028
11029         /**
11030          * Returns true if the element has any effects actively running or queued, else returns false.
11031          * @return {Boolean} True if element has active effects, else false
11032          */
11033     hasActiveFx : function(){
11034         return this.fxQueue && this.fxQueue[0];
11035     },
11036
11037         /**
11038          * Stops any running effects and clears the element's internal effects queue if it contains
11039          * any additional effects that haven't started yet.
11040          * @return {Roo.Element} The Element
11041          */
11042     stopFx : function(){
11043         if(this.hasActiveFx()){
11044             var cur = this.fxQueue[0];
11045             if(cur && cur.anim && cur.anim.isAnimated()){
11046                 this.fxQueue = [cur]; // clear out others
11047                 cur.anim.stop(true);
11048             }
11049         }
11050         return this;
11051     },
11052
11053         /* @private */
11054     beforeFx : function(o){
11055         if(this.hasActiveFx() && !o.concurrent){
11056            if(o.stopFx){
11057                this.stopFx();
11058                return true;
11059            }
11060            return false;
11061         }
11062         return true;
11063     },
11064
11065         /**
11066          * Returns true if the element is currently blocking so that no other effect can be queued
11067          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11068          * used to ensure that an effect initiated by a user action runs to completion prior to the
11069          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11070          * @return {Boolean} True if blocking, else false
11071          */
11072     hasFxBlock : function(){
11073         var q = this.fxQueue;
11074         return q && q[0] && q[0].block;
11075     },
11076
11077         /* @private */
11078     queueFx : function(o, fn){
11079         if(!this.fxQueue){
11080             this.fxQueue = [];
11081         }
11082         if(!this.hasFxBlock()){
11083             Roo.applyIf(o, this.fxDefaults);
11084             if(!o.concurrent){
11085                 var run = this.beforeFx(o);
11086                 fn.block = o.block;
11087                 this.fxQueue.push(fn);
11088                 if(run){
11089                     this.nextFx();
11090                 }
11091             }else{
11092                 fn.call(this);
11093             }
11094         }
11095         return this;
11096     },
11097
11098         /* @private */
11099     fxWrap : function(pos, o, vis){
11100         var wrap;
11101         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11102             var wrapXY;
11103             if(o.fixPosition){
11104                 wrapXY = this.getXY();
11105             }
11106             var div = document.createElement("div");
11107             div.style.visibility = vis;
11108             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11109             wrap.setPositioning(pos);
11110             if(wrap.getStyle("position") == "static"){
11111                 wrap.position("relative");
11112             }
11113             this.clearPositioning('auto');
11114             wrap.clip();
11115             wrap.dom.appendChild(this.dom);
11116             if(wrapXY){
11117                 wrap.setXY(wrapXY);
11118             }
11119         }
11120         return wrap;
11121     },
11122
11123         /* @private */
11124     fxUnwrap : function(wrap, pos, o){
11125         this.clearPositioning();
11126         this.setPositioning(pos);
11127         if(!o.wrap){
11128             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11129             wrap.remove();
11130         }
11131     },
11132
11133         /* @private */
11134     getFxRestore : function(){
11135         var st = this.dom.style;
11136         return {pos: this.getPositioning(), width: st.width, height : st.height};
11137     },
11138
11139         /* @private */
11140     afterFx : function(o){
11141         if(o.afterStyle){
11142             this.applyStyles(o.afterStyle);
11143         }
11144         if(o.afterCls){
11145             this.addClass(o.afterCls);
11146         }
11147         if(o.remove === true){
11148             this.remove();
11149         }
11150         Roo.callback(o.callback, o.scope, [this]);
11151         if(!o.concurrent){
11152             this.fxQueue.shift();
11153             this.nextFx();
11154         }
11155     },
11156
11157         /* @private */
11158     getFxEl : function(){ // support for composite element fx
11159         return Roo.get(this.dom);
11160     },
11161
11162         /* @private */
11163     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11164         animType = animType || 'run';
11165         opt = opt || {};
11166         var anim = Roo.lib.Anim[animType](
11167             this.dom, args,
11168             (opt.duration || defaultDur) || .35,
11169             (opt.easing || defaultEase) || 'easeOut',
11170             function(){
11171                 Roo.callback(cb, this);
11172             },
11173             this
11174         );
11175         opt.anim = anim;
11176         return anim;
11177     }
11178 };
11179
11180 // backwords compat
11181 Roo.Fx.resize = Roo.Fx.scale;
11182
11183 //When included, Roo.Fx is automatically applied to Element so that all basic
11184 //effects are available directly via the Element API
11185 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11186  * Based on:
11187  * Ext JS Library 1.1.1
11188  * Copyright(c) 2006-2007, Ext JS, LLC.
11189  *
11190  * Originally Released Under LGPL - original licence link has changed is not relivant.
11191  *
11192  * Fork - LGPL
11193  * <script type="text/javascript">
11194  */
11195
11196
11197 /**
11198  * @class Roo.CompositeElement
11199  * Standard composite class. Creates a Roo.Element for every element in the collection.
11200  * <br><br>
11201  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11202  * actions will be performed on all the elements in this collection.</b>
11203  * <br><br>
11204  * All methods return <i>this</i> and can be chained.
11205  <pre><code>
11206  var els = Roo.select("#some-el div.some-class", true);
11207  // or select directly from an existing element
11208  var el = Roo.get('some-el');
11209  el.select('div.some-class', true);
11210
11211  els.setWidth(100); // all elements become 100 width
11212  els.hide(true); // all elements fade out and hide
11213  // or
11214  els.setWidth(100).hide(true);
11215  </code></pre>
11216  */
11217 Roo.CompositeElement = function(els){
11218     this.elements = [];
11219     this.addElements(els);
11220 };
11221 Roo.CompositeElement.prototype = {
11222     isComposite: true,
11223     addElements : function(els){
11224         if(!els) {
11225             return this;
11226         }
11227         if(typeof els == "string"){
11228             els = Roo.Element.selectorFunction(els);
11229         }
11230         var yels = this.elements;
11231         var index = yels.length-1;
11232         for(var i = 0, len = els.length; i < len; i++) {
11233                 yels[++index] = Roo.get(els[i]);
11234         }
11235         return this;
11236     },
11237
11238     /**
11239     * Clears this composite and adds the elements returned by the passed selector.
11240     * @param {String/Array} els A string CSS selector, an array of elements or an element
11241     * @return {CompositeElement} this
11242     */
11243     fill : function(els){
11244         this.elements = [];
11245         this.add(els);
11246         return this;
11247     },
11248
11249     /**
11250     * Filters this composite to only elements that match the passed selector.
11251     * @param {String} selector A string CSS selector
11252     * @param {Boolean} inverse return inverse filter (not matches)
11253     * @return {CompositeElement} this
11254     */
11255     filter : function(selector, inverse){
11256         var els = [];
11257         inverse = inverse || false;
11258         this.each(function(el){
11259             var match = inverse ? !el.is(selector) : el.is(selector);
11260             if(match){
11261                 els[els.length] = el.dom;
11262             }
11263         });
11264         this.fill(els);
11265         return this;
11266     },
11267
11268     invoke : function(fn, args){
11269         var els = this.elements;
11270         for(var i = 0, len = els.length; i < len; i++) {
11271                 Roo.Element.prototype[fn].apply(els[i], args);
11272         }
11273         return this;
11274     },
11275     /**
11276     * Adds elements to this composite.
11277     * @param {String/Array} els A string CSS selector, an array of elements or an element
11278     * @return {CompositeElement} this
11279     */
11280     add : function(els){
11281         if(typeof els == "string"){
11282             this.addElements(Roo.Element.selectorFunction(els));
11283         }else if(els.length !== undefined){
11284             this.addElements(els);
11285         }else{
11286             this.addElements([els]);
11287         }
11288         return this;
11289     },
11290     /**
11291     * Calls the passed function passing (el, this, index) for each element in this composite.
11292     * @param {Function} fn The function to call
11293     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11294     * @return {CompositeElement} this
11295     */
11296     each : function(fn, scope){
11297         var els = this.elements;
11298         for(var i = 0, len = els.length; i < len; i++){
11299             if(fn.call(scope || els[i], els[i], this, i) === false) {
11300                 break;
11301             }
11302         }
11303         return this;
11304     },
11305
11306     /**
11307      * Returns the Element object at the specified index
11308      * @param {Number} index
11309      * @return {Roo.Element}
11310      */
11311     item : function(index){
11312         return this.elements[index] || null;
11313     },
11314
11315     /**
11316      * Returns the first Element
11317      * @return {Roo.Element}
11318      */
11319     first : function(){
11320         return this.item(0);
11321     },
11322
11323     /**
11324      * Returns the last Element
11325      * @return {Roo.Element}
11326      */
11327     last : function(){
11328         return this.item(this.elements.length-1);
11329     },
11330
11331     /**
11332      * Returns the number of elements in this composite
11333      * @return Number
11334      */
11335     getCount : function(){
11336         return this.elements.length;
11337     },
11338
11339     /**
11340      * Returns true if this composite contains the passed element
11341      * @return Boolean
11342      */
11343     contains : function(el){
11344         return this.indexOf(el) !== -1;
11345     },
11346
11347     /**
11348      * Returns true if this composite contains the passed element
11349      * @return Boolean
11350      */
11351     indexOf : function(el){
11352         return this.elements.indexOf(Roo.get(el));
11353     },
11354
11355
11356     /**
11357     * Removes the specified element(s).
11358     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11359     * or an array of any of those.
11360     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11361     * @return {CompositeElement} this
11362     */
11363     removeElement : function(el, removeDom){
11364         if(el instanceof Array){
11365             for(var i = 0, len = el.length; i < len; i++){
11366                 this.removeElement(el[i]);
11367             }
11368             return this;
11369         }
11370         var index = typeof el == 'number' ? el : this.indexOf(el);
11371         if(index !== -1){
11372             if(removeDom){
11373                 var d = this.elements[index];
11374                 if(d.dom){
11375                     d.remove();
11376                 }else{
11377                     d.parentNode.removeChild(d);
11378                 }
11379             }
11380             this.elements.splice(index, 1);
11381         }
11382         return this;
11383     },
11384
11385     /**
11386     * Replaces the specified element with the passed element.
11387     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11388     * to replace.
11389     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11390     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11391     * @return {CompositeElement} this
11392     */
11393     replaceElement : function(el, replacement, domReplace){
11394         var index = typeof el == 'number' ? el : this.indexOf(el);
11395         if(index !== -1){
11396             if(domReplace){
11397                 this.elements[index].replaceWith(replacement);
11398             }else{
11399                 this.elements.splice(index, 1, Roo.get(replacement))
11400             }
11401         }
11402         return this;
11403     },
11404
11405     /**
11406      * Removes all elements.
11407      */
11408     clear : function(){
11409         this.elements = [];
11410     }
11411 };
11412 (function(){
11413     Roo.CompositeElement.createCall = function(proto, fnName){
11414         if(!proto[fnName]){
11415             proto[fnName] = function(){
11416                 return this.invoke(fnName, arguments);
11417             };
11418         }
11419     };
11420     for(var fnName in Roo.Element.prototype){
11421         if(typeof Roo.Element.prototype[fnName] == "function"){
11422             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11423         }
11424     };
11425 })();
11426 /*
11427  * Based on:
11428  * Ext JS Library 1.1.1
11429  * Copyright(c) 2006-2007, Ext JS, LLC.
11430  *
11431  * Originally Released Under LGPL - original licence link has changed is not relivant.
11432  *
11433  * Fork - LGPL
11434  * <script type="text/javascript">
11435  */
11436
11437 /**
11438  * @class Roo.CompositeElementLite
11439  * @extends Roo.CompositeElement
11440  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11441  <pre><code>
11442  var els = Roo.select("#some-el div.some-class");
11443  // or select directly from an existing element
11444  var el = Roo.get('some-el');
11445  el.select('div.some-class');
11446
11447  els.setWidth(100); // all elements become 100 width
11448  els.hide(true); // all elements fade out and hide
11449  // or
11450  els.setWidth(100).hide(true);
11451  </code></pre><br><br>
11452  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11453  * actions will be performed on all the elements in this collection.</b>
11454  */
11455 Roo.CompositeElementLite = function(els){
11456     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11457     this.el = new Roo.Element.Flyweight();
11458 };
11459 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11460     addElements : function(els){
11461         if(els){
11462             if(els instanceof Array){
11463                 this.elements = this.elements.concat(els);
11464             }else{
11465                 var yels = this.elements;
11466                 var index = yels.length-1;
11467                 for(var i = 0, len = els.length; i < len; i++) {
11468                     yels[++index] = els[i];
11469                 }
11470             }
11471         }
11472         return this;
11473     },
11474     invoke : function(fn, args){
11475         var els = this.elements;
11476         var el = this.el;
11477         for(var i = 0, len = els.length; i < len; i++) {
11478             el.dom = els[i];
11479                 Roo.Element.prototype[fn].apply(el, args);
11480         }
11481         return this;
11482     },
11483     /**
11484      * Returns a flyweight Element of the dom element object at the specified index
11485      * @param {Number} index
11486      * @return {Roo.Element}
11487      */
11488     item : function(index){
11489         if(!this.elements[index]){
11490             return null;
11491         }
11492         this.el.dom = this.elements[index];
11493         return this.el;
11494     },
11495
11496     // fixes scope with flyweight
11497     addListener : function(eventName, handler, scope, opt){
11498         var els = this.elements;
11499         for(var i = 0, len = els.length; i < len; i++) {
11500             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11501         }
11502         return this;
11503     },
11504
11505     /**
11506     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11507     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11508     * a reference to the dom node, use el.dom.</b>
11509     * @param {Function} fn The function to call
11510     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11511     * @return {CompositeElement} this
11512     */
11513     each : function(fn, scope){
11514         var els = this.elements;
11515         var el = this.el;
11516         for(var i = 0, len = els.length; i < len; i++){
11517             el.dom = els[i];
11518                 if(fn.call(scope || el, el, this, i) === false){
11519                 break;
11520             }
11521         }
11522         return this;
11523     },
11524
11525     indexOf : function(el){
11526         return this.elements.indexOf(Roo.getDom(el));
11527     },
11528
11529     replaceElement : function(el, replacement, domReplace){
11530         var index = typeof el == 'number' ? el : this.indexOf(el);
11531         if(index !== -1){
11532             replacement = Roo.getDom(replacement);
11533             if(domReplace){
11534                 var d = this.elements[index];
11535                 d.parentNode.insertBefore(replacement, d);
11536                 d.parentNode.removeChild(d);
11537             }
11538             this.elements.splice(index, 1, replacement);
11539         }
11540         return this;
11541     }
11542 });
11543 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11544
11545 /*
11546  * Based on:
11547  * Ext JS Library 1.1.1
11548  * Copyright(c) 2006-2007, Ext JS, LLC.
11549  *
11550  * Originally Released Under LGPL - original licence link has changed is not relivant.
11551  *
11552  * Fork - LGPL
11553  * <script type="text/javascript">
11554  */
11555
11556  
11557
11558 /**
11559  * @class Roo.data.Connection
11560  * @extends Roo.util.Observable
11561  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11562  * either to a configured URL, or to a URL specified at request time. 
11563  * 
11564  * Requests made by this class are asynchronous, and will return immediately. No data from
11565  * the server will be available to the statement immediately following the {@link #request} call.
11566  * To process returned data, use a callback in the request options object, or an event listener.
11567  * 
11568  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11569  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11570  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11571  * property and, if present, the IFRAME's XML document as the responseXML property.
11572  * 
11573  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11574  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11575  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11576  * standard DOM methods.
11577  * @constructor
11578  * @param {Object} config a configuration object.
11579  */
11580 Roo.data.Connection = function(config){
11581     Roo.apply(this, config);
11582     this.addEvents({
11583         /**
11584          * @event beforerequest
11585          * Fires before a network request is made to retrieve a data object.
11586          * @param {Connection} conn This Connection object.
11587          * @param {Object} options The options config object passed to the {@link #request} method.
11588          */
11589         "beforerequest" : true,
11590         /**
11591          * @event requestcomplete
11592          * Fires if the request was successfully completed.
11593          * @param {Connection} conn This Connection object.
11594          * @param {Object} response The XHR object containing the response data.
11595          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11596          * @param {Object} options The options config object passed to the {@link #request} method.
11597          */
11598         "requestcomplete" : true,
11599         /**
11600          * @event requestexception
11601          * Fires if an error HTTP status was returned from the server.
11602          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11603          * @param {Connection} conn This Connection object.
11604          * @param {Object} response The XHR object containing the response data.
11605          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11606          * @param {Object} options The options config object passed to the {@link #request} method.
11607          */
11608         "requestexception" : true
11609     });
11610     Roo.data.Connection.superclass.constructor.call(this);
11611 };
11612
11613 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11614     /**
11615      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11616      */
11617     /**
11618      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11619      * extra parameters to each request made by this object. (defaults to undefined)
11620      */
11621     /**
11622      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11623      *  to each request made by this object. (defaults to undefined)
11624      */
11625     /**
11626      * @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)
11627      */
11628     /**
11629      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11630      */
11631     timeout : 30000,
11632     /**
11633      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11634      * @type Boolean
11635      */
11636     autoAbort:false,
11637
11638     /**
11639      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11640      * @type Boolean
11641      */
11642     disableCaching: true,
11643
11644     /**
11645      * Sends an HTTP request to a remote server.
11646      * @param {Object} options An object which may contain the following properties:<ul>
11647      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11648      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11649      * request, a url encoded string or a function to call to get either.</li>
11650      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11651      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11652      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11653      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11654      * <li>options {Object} The parameter to the request call.</li>
11655      * <li>success {Boolean} True if the request succeeded.</li>
11656      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11657      * </ul></li>
11658      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11659      * The callback is passed the following parameters:<ul>
11660      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11661      * <li>options {Object} The parameter to the request call.</li>
11662      * </ul></li>
11663      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11664      * The callback is passed the following parameters:<ul>
11665      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11666      * <li>options {Object} The parameter to the request call.</li>
11667      * </ul></li>
11668      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11669      * for the callback function. Defaults to the browser window.</li>
11670      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11671      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11672      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11673      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11674      * params for the post data. Any params will be appended to the URL.</li>
11675      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11676      * </ul>
11677      * @return {Number} transactionId
11678      */
11679     request : function(o){
11680         if(this.fireEvent("beforerequest", this, o) !== false){
11681             var p = o.params;
11682
11683             if(typeof p == "function"){
11684                 p = p.call(o.scope||window, o);
11685             }
11686             if(typeof p == "object"){
11687                 p = Roo.urlEncode(o.params);
11688             }
11689             if(this.extraParams){
11690                 var extras = Roo.urlEncode(this.extraParams);
11691                 p = p ? (p + '&' + extras) : extras;
11692             }
11693
11694             var url = o.url || this.url;
11695             if(typeof url == 'function'){
11696                 url = url.call(o.scope||window, o);
11697             }
11698
11699             if(o.form){
11700                 var form = Roo.getDom(o.form);
11701                 url = url || form.action;
11702
11703                 var enctype = form.getAttribute("enctype");
11704                 
11705                 if (o.formData) {
11706                     return this.doFormDataUpload(o, url);
11707                 }
11708                 
11709                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11710                     return this.doFormUpload(o, p, url);
11711                 }
11712                 var f = Roo.lib.Ajax.serializeForm(form);
11713                 p = p ? (p + '&' + f) : f;
11714             }
11715             
11716             if (!o.form && o.formData) {
11717                 o.formData = o.formData === true ? new FormData() : o.formData;
11718                 for (var k in o.params) {
11719                     o.formData.append(k,o.params[k]);
11720                 }
11721                     
11722                 return this.doFormDataUpload(o, url);
11723             }
11724             
11725
11726             var hs = o.headers;
11727             if(this.defaultHeaders){
11728                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11729                 if(!o.headers){
11730                     o.headers = hs;
11731                 }
11732             }
11733
11734             var cb = {
11735                 success: this.handleResponse,
11736                 failure: this.handleFailure,
11737                 scope: this,
11738                 argument: {options: o},
11739                 timeout : o.timeout || this.timeout
11740             };
11741
11742             var method = o.method||this.method||(p ? "POST" : "GET");
11743
11744             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11745                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11746             }
11747
11748             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11749                 if(o.autoAbort){
11750                     this.abort();
11751                 }
11752             }else if(this.autoAbort !== false){
11753                 this.abort();
11754             }
11755
11756             if((method == 'GET' && p) || o.xmlData){
11757                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11758                 p = '';
11759             }
11760             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11761             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11762             Roo.lib.Ajax.useDefaultHeader == true;
11763             return this.transId;
11764         }else{
11765             Roo.callback(o.callback, o.scope, [o, null, null]);
11766             return null;
11767         }
11768     },
11769
11770     /**
11771      * Determine whether this object has a request outstanding.
11772      * @param {Number} transactionId (Optional) defaults to the last transaction
11773      * @return {Boolean} True if there is an outstanding request.
11774      */
11775     isLoading : function(transId){
11776         if(transId){
11777             return Roo.lib.Ajax.isCallInProgress(transId);
11778         }else{
11779             return this.transId ? true : false;
11780         }
11781     },
11782
11783     /**
11784      * Aborts any outstanding request.
11785      * @param {Number} transactionId (Optional) defaults to the last transaction
11786      */
11787     abort : function(transId){
11788         if(transId || this.isLoading()){
11789             Roo.lib.Ajax.abort(transId || this.transId);
11790         }
11791     },
11792
11793     // private
11794     handleResponse : function(response){
11795         this.transId = false;
11796         var options = response.argument.options;
11797         response.argument = options ? options.argument : null;
11798         this.fireEvent("requestcomplete", this, response, options);
11799         Roo.callback(options.success, options.scope, [response, options]);
11800         Roo.callback(options.callback, options.scope, [options, true, response]);
11801     },
11802
11803     // private
11804     handleFailure : function(response, e){
11805         this.transId = false;
11806         var options = response.argument.options;
11807         response.argument = options ? options.argument : null;
11808         this.fireEvent("requestexception", this, response, options, e);
11809         Roo.callback(options.failure, options.scope, [response, options]);
11810         Roo.callback(options.callback, options.scope, [options, false, response]);
11811     },
11812
11813     // private
11814     doFormUpload : function(o, ps, url){
11815         var id = Roo.id();
11816         var frame = document.createElement('iframe');
11817         frame.id = id;
11818         frame.name = id;
11819         frame.className = 'x-hidden';
11820         if(Roo.isIE){
11821             frame.src = Roo.SSL_SECURE_URL;
11822         }
11823         document.body.appendChild(frame);
11824
11825         if(Roo.isIE){
11826            document.frames[id].name = id;
11827         }
11828
11829         var form = Roo.getDom(o.form);
11830         form.target = id;
11831         form.method = 'POST';
11832         form.enctype = form.encoding = 'multipart/form-data';
11833         if(url){
11834             form.action = url;
11835         }
11836
11837         var hiddens, hd;
11838         if(ps){ // add dynamic params
11839             hiddens = [];
11840             ps = Roo.urlDecode(ps, false);
11841             for(var k in ps){
11842                 if(ps.hasOwnProperty(k)){
11843                     hd = document.createElement('input');
11844                     hd.type = 'hidden';
11845                     hd.name = k;
11846                     hd.value = ps[k];
11847                     form.appendChild(hd);
11848                     hiddens.push(hd);
11849                 }
11850             }
11851         }
11852
11853         function cb(){
11854             var r = {  // bogus response object
11855                 responseText : '',
11856                 responseXML : null
11857             };
11858
11859             r.argument = o ? o.argument : null;
11860
11861             try { //
11862                 var doc;
11863                 if(Roo.isIE){
11864                     doc = frame.contentWindow.document;
11865                 }else {
11866                     doc = (frame.contentDocument || window.frames[id].document);
11867                 }
11868                 if(doc && doc.body){
11869                     r.responseText = doc.body.innerHTML;
11870                 }
11871                 if(doc && doc.XMLDocument){
11872                     r.responseXML = doc.XMLDocument;
11873                 }else {
11874                     r.responseXML = doc;
11875                 }
11876             }
11877             catch(e) {
11878                 // ignore
11879             }
11880
11881             Roo.EventManager.removeListener(frame, 'load', cb, this);
11882
11883             this.fireEvent("requestcomplete", this, r, o);
11884             Roo.callback(o.success, o.scope, [r, o]);
11885             Roo.callback(o.callback, o.scope, [o, true, r]);
11886
11887             setTimeout(function(){document.body.removeChild(frame);}, 100);
11888         }
11889
11890         Roo.EventManager.on(frame, 'load', cb, this);
11891         form.submit();
11892
11893         if(hiddens){ // remove dynamic params
11894             for(var i = 0, len = hiddens.length; i < len; i++){
11895                 form.removeChild(hiddens[i]);
11896             }
11897         }
11898     },
11899     // this is a 'formdata version???'
11900     
11901     
11902     doFormDataUpload : function(o,  url)
11903     {
11904         var formData;
11905         if (o.form) {
11906             var form =  Roo.getDom(o.form);
11907             form.enctype = form.encoding = 'multipart/form-data';
11908             formData = o.formData === true ? new FormData(form) : o.formData;
11909         } else {
11910             formData = o.formData === true ? new FormData() : o.formData;
11911         }
11912         
11913       
11914         var cb = {
11915             success: this.handleResponse,
11916             failure: this.handleFailure,
11917             scope: this,
11918             argument: {options: o},
11919             timeout : o.timeout || this.timeout
11920         };
11921  
11922         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11923             if(o.autoAbort){
11924                 this.abort();
11925             }
11926         }else if(this.autoAbort !== false){
11927             this.abort();
11928         }
11929
11930         //Roo.lib.Ajax.defaultPostHeader = null;
11931         Roo.lib.Ajax.useDefaultHeader = false;
11932         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11933         Roo.lib.Ajax.useDefaultHeader = true;
11934  
11935          
11936     }
11937     
11938 });
11939 /*
11940  * Based on:
11941  * Ext JS Library 1.1.1
11942  * Copyright(c) 2006-2007, Ext JS, LLC.
11943  *
11944  * Originally Released Under LGPL - original licence link has changed is not relivant.
11945  *
11946  * Fork - LGPL
11947  * <script type="text/javascript">
11948  */
11949  
11950 /**
11951  * Global Ajax request class.
11952  * 
11953  * @class Roo.Ajax
11954  * @extends Roo.data.Connection
11955  * @static
11956  * 
11957  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11958  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11959  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11960  * @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)
11961  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11962  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11963  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11964  */
11965 Roo.Ajax = new Roo.data.Connection({
11966     // fix up the docs
11967     /**
11968      * @scope Roo.Ajax
11969      * @type {Boolear} 
11970      */
11971     autoAbort : false,
11972
11973     /**
11974      * Serialize the passed form into a url encoded string
11975      * @scope Roo.Ajax
11976      * @param {String/HTMLElement} form
11977      * @return {String}
11978      */
11979     serializeForm : function(form){
11980         return Roo.lib.Ajax.serializeForm(form);
11981     }
11982 });/*
11983  * Based on:
11984  * Ext JS Library 1.1.1
11985  * Copyright(c) 2006-2007, Ext JS, LLC.
11986  *
11987  * Originally Released Under LGPL - original licence link has changed is not relivant.
11988  *
11989  * Fork - LGPL
11990  * <script type="text/javascript">
11991  */
11992
11993  
11994 /**
11995  * @class Roo.UpdateManager
11996  * @extends Roo.util.Observable
11997  * Provides AJAX-style update for Element object.<br><br>
11998  * Usage:<br>
11999  * <pre><code>
12000  * // Get it from a Roo.Element object
12001  * var el = Roo.get("foo");
12002  * var mgr = el.getUpdateManager();
12003  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12004  * ...
12005  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12006  * <br>
12007  * // or directly (returns the same UpdateManager instance)
12008  * var mgr = new Roo.UpdateManager("myElementId");
12009  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12010  * mgr.on("update", myFcnNeedsToKnow);
12011  * <br>
12012    // short handed call directly from the element object
12013    Roo.get("foo").load({
12014         url: "bar.php",
12015         scripts:true,
12016         params: "for=bar",
12017         text: "Loading Foo..."
12018    });
12019  * </code></pre>
12020  * @constructor
12021  * Create new UpdateManager directly.
12022  * @param {String/HTMLElement/Roo.Element} el The element to update
12023  * @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).
12024  */
12025 Roo.UpdateManager = function(el, forceNew){
12026     el = Roo.get(el);
12027     if(!forceNew && el.updateManager){
12028         return el.updateManager;
12029     }
12030     /**
12031      * The Element object
12032      * @type Roo.Element
12033      */
12034     this.el = el;
12035     /**
12036      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12037      * @type String
12038      */
12039     this.defaultUrl = null;
12040
12041     this.addEvents({
12042         /**
12043          * @event beforeupdate
12044          * Fired before an update is made, return false from your handler and the update is cancelled.
12045          * @param {Roo.Element} el
12046          * @param {String/Object/Function} url
12047          * @param {String/Object} params
12048          */
12049         "beforeupdate": true,
12050         /**
12051          * @event update
12052          * Fired after successful update is made.
12053          * @param {Roo.Element} el
12054          * @param {Object} oResponseObject The response Object
12055          */
12056         "update": true,
12057         /**
12058          * @event failure
12059          * Fired on update failure.
12060          * @param {Roo.Element} el
12061          * @param {Object} oResponseObject The response Object
12062          */
12063         "failure": true
12064     });
12065     var d = Roo.UpdateManager.defaults;
12066     /**
12067      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12068      * @type String
12069      */
12070     this.sslBlankUrl = d.sslBlankUrl;
12071     /**
12072      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12073      * @type Boolean
12074      */
12075     this.disableCaching = d.disableCaching;
12076     /**
12077      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12078      * @type String
12079      */
12080     this.indicatorText = d.indicatorText;
12081     /**
12082      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12083      * @type String
12084      */
12085     this.showLoadIndicator = d.showLoadIndicator;
12086     /**
12087      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12088      * @type Number
12089      */
12090     this.timeout = d.timeout;
12091
12092     /**
12093      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12094      * @type Boolean
12095      */
12096     this.loadScripts = d.loadScripts;
12097
12098     /**
12099      * Transaction object of current executing transaction
12100      */
12101     this.transaction = null;
12102
12103     /**
12104      * @private
12105      */
12106     this.autoRefreshProcId = null;
12107     /**
12108      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12109      * @type Function
12110      */
12111     this.refreshDelegate = this.refresh.createDelegate(this);
12112     /**
12113      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12114      * @type Function
12115      */
12116     this.updateDelegate = this.update.createDelegate(this);
12117     /**
12118      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12119      * @type Function
12120      */
12121     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12122     /**
12123      * @private
12124      */
12125     this.successDelegate = this.processSuccess.createDelegate(this);
12126     /**
12127      * @private
12128      */
12129     this.failureDelegate = this.processFailure.createDelegate(this);
12130
12131     if(!this.renderer){
12132      /**
12133       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12134       */
12135     this.renderer = new Roo.UpdateManager.BasicRenderer();
12136     }
12137     
12138     Roo.UpdateManager.superclass.constructor.call(this);
12139 };
12140
12141 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12142     /**
12143      * Get the Element this UpdateManager is bound to
12144      * @return {Roo.Element} The element
12145      */
12146     getEl : function(){
12147         return this.el;
12148     },
12149     /**
12150      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12151      * @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:
12152 <pre><code>
12153 um.update({<br/>
12154     url: "your-url.php",<br/>
12155     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12156     callback: yourFunction,<br/>
12157     scope: yourObject, //(optional scope)  <br/>
12158     discardUrl: false, <br/>
12159     nocache: false,<br/>
12160     text: "Loading...",<br/>
12161     timeout: 30,<br/>
12162     scripts: false<br/>
12163 });
12164 </code></pre>
12165      * The only required property is url. The optional properties nocache, text and scripts
12166      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12167      * @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}
12168      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12169      * @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.
12170      */
12171     update : function(url, params, callback, discardUrl){
12172         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12173             var method = this.method,
12174                 cfg;
12175             if(typeof url == "object"){ // must be config object
12176                 cfg = url;
12177                 url = cfg.url;
12178                 params = params || cfg.params;
12179                 callback = callback || cfg.callback;
12180                 discardUrl = discardUrl || cfg.discardUrl;
12181                 if(callback && cfg.scope){
12182                     callback = callback.createDelegate(cfg.scope);
12183                 }
12184                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12185                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12186                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12187                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12188                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12189             }
12190             this.showLoading();
12191             if(!discardUrl){
12192                 this.defaultUrl = url;
12193             }
12194             if(typeof url == "function"){
12195                 url = url.call(this);
12196             }
12197
12198             method = method || (params ? "POST" : "GET");
12199             if(method == "GET"){
12200                 url = this.prepareUrl(url);
12201             }
12202
12203             var o = Roo.apply(cfg ||{}, {
12204                 url : url,
12205                 params: params,
12206                 success: this.successDelegate,
12207                 failure: this.failureDelegate,
12208                 callback: undefined,
12209                 timeout: (this.timeout*1000),
12210                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12211             });
12212             Roo.log("updated manager called with timeout of " + o.timeout);
12213             this.transaction = Roo.Ajax.request(o);
12214         }
12215     },
12216
12217     /**
12218      * 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.
12219      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12220      * @param {String/HTMLElement} form The form Id or form element
12221      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12222      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12223      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12224      */
12225     formUpdate : function(form, url, reset, callback){
12226         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12227             if(typeof url == "function"){
12228                 url = url.call(this);
12229             }
12230             form = Roo.getDom(form);
12231             this.transaction = Roo.Ajax.request({
12232                 form: form,
12233                 url:url,
12234                 success: this.successDelegate,
12235                 failure: this.failureDelegate,
12236                 timeout: (this.timeout*1000),
12237                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12238             });
12239             this.showLoading.defer(1, this);
12240         }
12241     },
12242
12243     /**
12244      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12245      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12246      */
12247     refresh : function(callback){
12248         if(this.defaultUrl == null){
12249             return;
12250         }
12251         this.update(this.defaultUrl, null, callback, true);
12252     },
12253
12254     /**
12255      * Set this element to auto refresh.
12256      * @param {Number} interval How often to update (in seconds).
12257      * @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)
12258      * @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}
12259      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12260      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12261      */
12262     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12263         if(refreshNow){
12264             this.update(url || this.defaultUrl, params, callback, true);
12265         }
12266         if(this.autoRefreshProcId){
12267             clearInterval(this.autoRefreshProcId);
12268         }
12269         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12270     },
12271
12272     /**
12273      * Stop auto refresh on this element.
12274      */
12275      stopAutoRefresh : function(){
12276         if(this.autoRefreshProcId){
12277             clearInterval(this.autoRefreshProcId);
12278             delete this.autoRefreshProcId;
12279         }
12280     },
12281
12282     isAutoRefreshing : function(){
12283        return this.autoRefreshProcId ? true : false;
12284     },
12285     /**
12286      * Called to update the element to "Loading" state. Override to perform custom action.
12287      */
12288     showLoading : function(){
12289         if(this.showLoadIndicator){
12290             this.el.update(this.indicatorText);
12291         }
12292     },
12293
12294     /**
12295      * Adds unique parameter to query string if disableCaching = true
12296      * @private
12297      */
12298     prepareUrl : function(url){
12299         if(this.disableCaching){
12300             var append = "_dc=" + (new Date().getTime());
12301             if(url.indexOf("?") !== -1){
12302                 url += "&" + append;
12303             }else{
12304                 url += "?" + append;
12305             }
12306         }
12307         return url;
12308     },
12309
12310     /**
12311      * @private
12312      */
12313     processSuccess : function(response){
12314         this.transaction = null;
12315         if(response.argument.form && response.argument.reset){
12316             try{ // put in try/catch since some older FF releases had problems with this
12317                 response.argument.form.reset();
12318             }catch(e){}
12319         }
12320         if(this.loadScripts){
12321             this.renderer.render(this.el, response, this,
12322                 this.updateComplete.createDelegate(this, [response]));
12323         }else{
12324             this.renderer.render(this.el, response, this);
12325             this.updateComplete(response);
12326         }
12327     },
12328
12329     updateComplete : function(response){
12330         this.fireEvent("update", this.el, response);
12331         if(typeof response.argument.callback == "function"){
12332             response.argument.callback(this.el, true, response);
12333         }
12334     },
12335
12336     /**
12337      * @private
12338      */
12339     processFailure : function(response){
12340         this.transaction = null;
12341         this.fireEvent("failure", this.el, response);
12342         if(typeof response.argument.callback == "function"){
12343             response.argument.callback(this.el, false, response);
12344         }
12345     },
12346
12347     /**
12348      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12349      * @param {Object} renderer The object implementing the render() method
12350      */
12351     setRenderer : function(renderer){
12352         this.renderer = renderer;
12353     },
12354
12355     getRenderer : function(){
12356        return this.renderer;
12357     },
12358
12359     /**
12360      * Set the defaultUrl used for updates
12361      * @param {String/Function} defaultUrl The url or a function to call to get the url
12362      */
12363     setDefaultUrl : function(defaultUrl){
12364         this.defaultUrl = defaultUrl;
12365     },
12366
12367     /**
12368      * Aborts the executing transaction
12369      */
12370     abort : function(){
12371         if(this.transaction){
12372             Roo.Ajax.abort(this.transaction);
12373         }
12374     },
12375
12376     /**
12377      * Returns true if an update is in progress
12378      * @return {Boolean}
12379      */
12380     isUpdating : function(){
12381         if(this.transaction){
12382             return Roo.Ajax.isLoading(this.transaction);
12383         }
12384         return false;
12385     }
12386 });
12387
12388 /**
12389  * @class Roo.UpdateManager.defaults
12390  * @static (not really - but it helps the doc tool)
12391  * The defaults collection enables customizing the default properties of UpdateManager
12392  */
12393    Roo.UpdateManager.defaults = {
12394        /**
12395          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12396          * @type Number
12397          */
12398          timeout : 30,
12399
12400          /**
12401          * True to process scripts by default (Defaults to false).
12402          * @type Boolean
12403          */
12404         loadScripts : false,
12405
12406         /**
12407         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12408         * @type String
12409         */
12410         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12411         /**
12412          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12413          * @type Boolean
12414          */
12415         disableCaching : false,
12416         /**
12417          * Whether to show indicatorText when loading (Defaults to true).
12418          * @type Boolean
12419          */
12420         showLoadIndicator : true,
12421         /**
12422          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12423          * @type String
12424          */
12425         indicatorText : '<div class="loading-indicator">Loading...</div>'
12426    };
12427
12428 /**
12429  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12430  *Usage:
12431  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12432  * @param {String/HTMLElement/Roo.Element} el The element to update
12433  * @param {String} url The url
12434  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12435  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12436  * @static
12437  * @deprecated
12438  * @member Roo.UpdateManager
12439  */
12440 Roo.UpdateManager.updateElement = function(el, url, params, options){
12441     var um = Roo.get(el, true).getUpdateManager();
12442     Roo.apply(um, options);
12443     um.update(url, params, options ? options.callback : null);
12444 };
12445 // alias for backwards compat
12446 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12447 /**
12448  * @class Roo.UpdateManager.BasicRenderer
12449  * Default Content renderer. Updates the elements innerHTML with the responseText.
12450  */
12451 Roo.UpdateManager.BasicRenderer = function(){};
12452
12453 Roo.UpdateManager.BasicRenderer.prototype = {
12454     /**
12455      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12456      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12457      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12458      * @param {Roo.Element} el The element being rendered
12459      * @param {Object} response The YUI Connect response object
12460      * @param {UpdateManager} updateManager The calling update manager
12461      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12462      */
12463      render : function(el, response, updateManager, callback){
12464         el.update(response.responseText, updateManager.loadScripts, callback);
12465     }
12466 };
12467 /*
12468  * Based on:
12469  * Roo JS
12470  * (c)) Alan Knowles
12471  * Licence : LGPL
12472  */
12473
12474
12475 /**
12476  * @class Roo.DomTemplate
12477  * @extends Roo.Template
12478  * An effort at a dom based template engine..
12479  *
12480  * Similar to XTemplate, except it uses dom parsing to create the template..
12481  *
12482  * Supported features:
12483  *
12484  *  Tags:
12485
12486 <pre><code>
12487       {a_variable} - output encoded.
12488       {a_variable.format:("Y-m-d")} - call a method on the variable
12489       {a_variable:raw} - unencoded output
12490       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12491       {a_variable:this.method_on_template(...)} - call a method on the template object.
12492  
12493 </code></pre>
12494  *  The tpl tag:
12495 <pre><code>
12496         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12497         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12498         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12499         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12500   
12501 </code></pre>
12502  *      
12503  */
12504 Roo.DomTemplate = function()
12505 {
12506      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12507      if (this.html) {
12508         this.compile();
12509      }
12510 };
12511
12512
12513 Roo.extend(Roo.DomTemplate, Roo.Template, {
12514     /**
12515      * id counter for sub templates.
12516      */
12517     id : 0,
12518     /**
12519      * flag to indicate if dom parser is inside a pre,
12520      * it will strip whitespace if not.
12521      */
12522     inPre : false,
12523     
12524     /**
12525      * The various sub templates
12526      */
12527     tpls : false,
12528     
12529     
12530     
12531     /**
12532      *
12533      * basic tag replacing syntax
12534      * WORD:WORD()
12535      *
12536      * // you can fake an object call by doing this
12537      *  x.t:(test,tesT) 
12538      * 
12539      */
12540     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12541     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12542     
12543     iterChild : function (node, method) {
12544         
12545         var oldPre = this.inPre;
12546         if (node.tagName == 'PRE') {
12547             this.inPre = true;
12548         }
12549         for( var i = 0; i < node.childNodes.length; i++) {
12550             method.call(this, node.childNodes[i]);
12551         }
12552         this.inPre = oldPre;
12553     },
12554     
12555     
12556     
12557     /**
12558      * compile the template
12559      *
12560      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12561      *
12562      */
12563     compile: function()
12564     {
12565         var s = this.html;
12566         
12567         // covert the html into DOM...
12568         var doc = false;
12569         var div =false;
12570         try {
12571             doc = document.implementation.createHTMLDocument("");
12572             doc.documentElement.innerHTML =   this.html  ;
12573             div = doc.documentElement;
12574         } catch (e) {
12575             // old IE... - nasty -- it causes all sorts of issues.. with
12576             // images getting pulled from server..
12577             div = document.createElement('div');
12578             div.innerHTML = this.html;
12579         }
12580         //doc.documentElement.innerHTML = htmlBody
12581          
12582         
12583         
12584         this.tpls = [];
12585         var _t = this;
12586         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12587         
12588         var tpls = this.tpls;
12589         
12590         // create a top level template from the snippet..
12591         
12592         //Roo.log(div.innerHTML);
12593         
12594         var tpl = {
12595             uid : 'master',
12596             id : this.id++,
12597             attr : false,
12598             value : false,
12599             body : div.innerHTML,
12600             
12601             forCall : false,
12602             execCall : false,
12603             dom : div,
12604             isTop : true
12605             
12606         };
12607         tpls.unshift(tpl);
12608         
12609         
12610         // compile them...
12611         this.tpls = [];
12612         Roo.each(tpls, function(tp){
12613             this.compileTpl(tp);
12614             this.tpls[tp.id] = tp;
12615         }, this);
12616         
12617         this.master = tpls[0];
12618         return this;
12619         
12620         
12621     },
12622     
12623     compileNode : function(node, istop) {
12624         // test for
12625         //Roo.log(node);
12626         
12627         
12628         // skip anything not a tag..
12629         if (node.nodeType != 1) {
12630             if (node.nodeType == 3 && !this.inPre) {
12631                 // reduce white space..
12632                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12633                 
12634             }
12635             return;
12636         }
12637         
12638         var tpl = {
12639             uid : false,
12640             id : false,
12641             attr : false,
12642             value : false,
12643             body : '',
12644             
12645             forCall : false,
12646             execCall : false,
12647             dom : false,
12648             isTop : istop
12649             
12650             
12651         };
12652         
12653         
12654         switch(true) {
12655             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12656             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12657             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12658             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12659             // no default..
12660         }
12661         
12662         
12663         if (!tpl.attr) {
12664             // just itterate children..
12665             this.iterChild(node,this.compileNode);
12666             return;
12667         }
12668         tpl.uid = this.id++;
12669         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12670         node.removeAttribute('roo-'+ tpl.attr);
12671         if (tpl.attr != 'name') {
12672             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12673             node.parentNode.replaceChild(placeholder,  node);
12674         } else {
12675             
12676             var placeholder =  document.createElement('span');
12677             placeholder.className = 'roo-tpl-' + tpl.value;
12678             node.parentNode.replaceChild(placeholder,  node);
12679         }
12680         
12681         // parent now sees '{domtplXXXX}
12682         this.iterChild(node,this.compileNode);
12683         
12684         // we should now have node body...
12685         var div = document.createElement('div');
12686         div.appendChild(node);
12687         tpl.dom = node;
12688         // this has the unfortunate side effect of converting tagged attributes
12689         // eg. href="{...}" into %7C...%7D
12690         // this has been fixed by searching for those combo's although it's a bit hacky..
12691         
12692         
12693         tpl.body = div.innerHTML;
12694         
12695         
12696          
12697         tpl.id = tpl.uid;
12698         switch(tpl.attr) {
12699             case 'for' :
12700                 switch (tpl.value) {
12701                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12702                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12703                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12704                 }
12705                 break;
12706             
12707             case 'exec':
12708                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12709                 break;
12710             
12711             case 'if':     
12712                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12713                 break;
12714             
12715             case 'name':
12716                 tpl.id  = tpl.value; // replace non characters???
12717                 break;
12718             
12719         }
12720         
12721         
12722         this.tpls.push(tpl);
12723         
12724         
12725         
12726     },
12727     
12728     
12729     
12730     
12731     /**
12732      * Compile a segment of the template into a 'sub-template'
12733      *
12734      * 
12735      * 
12736      *
12737      */
12738     compileTpl : function(tpl)
12739     {
12740         var fm = Roo.util.Format;
12741         var useF = this.disableFormats !== true;
12742         
12743         var sep = Roo.isGecko ? "+\n" : ",\n";
12744         
12745         var undef = function(str) {
12746             Roo.debug && Roo.log("Property not found :"  + str);
12747             return '';
12748         };
12749           
12750         //Roo.log(tpl.body);
12751         
12752         
12753         
12754         var fn = function(m, lbrace, name, format, args)
12755         {
12756             //Roo.log("ARGS");
12757             //Roo.log(arguments);
12758             args = args ? args.replace(/\\'/g,"'") : args;
12759             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12760             if (typeof(format) == 'undefined') {
12761                 format =  'htmlEncode'; 
12762             }
12763             if (format == 'raw' ) {
12764                 format = false;
12765             }
12766             
12767             if(name.substr(0, 6) == 'domtpl'){
12768                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12769             }
12770             
12771             // build an array of options to determine if value is undefined..
12772             
12773             // basically get 'xxxx.yyyy' then do
12774             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12775             //    (function () { Roo.log("Property not found"); return ''; })() :
12776             //    ......
12777             
12778             var udef_ar = [];
12779             var lookfor = '';
12780             Roo.each(name.split('.'), function(st) {
12781                 lookfor += (lookfor.length ? '.': '') + st;
12782                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12783             });
12784             
12785             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12786             
12787             
12788             if(format && useF){
12789                 
12790                 args = args ? ',' + args : "";
12791                  
12792                 if(format.substr(0, 5) != "this."){
12793                     format = "fm." + format + '(';
12794                 }else{
12795                     format = 'this.call("'+ format.substr(5) + '", ';
12796                     args = ", values";
12797                 }
12798                 
12799                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12800             }
12801              
12802             if (args && args.length) {
12803                 // called with xxyx.yuu:(test,test)
12804                 // change to ()
12805                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12806             }
12807             // raw.. - :raw modifier..
12808             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12809             
12810         };
12811         var body;
12812         // branched to use + in gecko and [].join() in others
12813         if(Roo.isGecko){
12814             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12815                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12816                     "';};};";
12817         }else{
12818             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12819             body.push(tpl.body.replace(/(\r\n|\n)/g,
12820                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12821             body.push("'].join('');};};");
12822             body = body.join('');
12823         }
12824         
12825         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12826        
12827         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12828         eval(body);
12829         
12830         return this;
12831     },
12832      
12833     /**
12834      * same as applyTemplate, except it's done to one of the subTemplates
12835      * when using named templates, you can do:
12836      *
12837      * var str = pl.applySubTemplate('your-name', values);
12838      *
12839      * 
12840      * @param {Number} id of the template
12841      * @param {Object} values to apply to template
12842      * @param {Object} parent (normaly the instance of this object)
12843      */
12844     applySubTemplate : function(id, values, parent)
12845     {
12846         
12847         
12848         var t = this.tpls[id];
12849         
12850         
12851         try { 
12852             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12853                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12854                 return '';
12855             }
12856         } catch(e) {
12857             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12858             Roo.log(values);
12859           
12860             return '';
12861         }
12862         try { 
12863             
12864             if(t.execCall && t.execCall.call(this, values, parent)){
12865                 return '';
12866             }
12867         } catch(e) {
12868             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12869             Roo.log(values);
12870             return '';
12871         }
12872         
12873         try {
12874             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12875             parent = t.target ? values : parent;
12876             if(t.forCall && vs instanceof Array){
12877                 var buf = [];
12878                 for(var i = 0, len = vs.length; i < len; i++){
12879                     try {
12880                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12881                     } catch (e) {
12882                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12883                         Roo.log(e.body);
12884                         //Roo.log(t.compiled);
12885                         Roo.log(vs[i]);
12886                     }   
12887                 }
12888                 return buf.join('');
12889             }
12890         } catch (e) {
12891             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12892             Roo.log(values);
12893             return '';
12894         }
12895         try {
12896             return t.compiled.call(this, vs, parent);
12897         } catch (e) {
12898             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12899             Roo.log(e.body);
12900             //Roo.log(t.compiled);
12901             Roo.log(values);
12902             return '';
12903         }
12904     },
12905
12906    
12907
12908     applyTemplate : function(values){
12909         return this.master.compiled.call(this, values, {});
12910         //var s = this.subs;
12911     },
12912
12913     apply : function(){
12914         return this.applyTemplate.apply(this, arguments);
12915     }
12916
12917  });
12918
12919 Roo.DomTemplate.from = function(el){
12920     el = Roo.getDom(el);
12921     return new Roo.Domtemplate(el.value || el.innerHTML);
12922 };/*
12923  * Based on:
12924  * Ext JS Library 1.1.1
12925  * Copyright(c) 2006-2007, Ext JS, LLC.
12926  *
12927  * Originally Released Under LGPL - original licence link has changed is not relivant.
12928  *
12929  * Fork - LGPL
12930  * <script type="text/javascript">
12931  */
12932
12933 /**
12934  * @class Roo.util.DelayedTask
12935  * Provides a convenient method of performing setTimeout where a new
12936  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12937  * You can use this class to buffer
12938  * the keypress events for a certain number of milliseconds, and perform only if they stop
12939  * for that amount of time.
12940  * @constructor The parameters to this constructor serve as defaults and are not required.
12941  * @param {Function} fn (optional) The default function to timeout
12942  * @param {Object} scope (optional) The default scope of that timeout
12943  * @param {Array} args (optional) The default Array of arguments
12944  */
12945 Roo.util.DelayedTask = function(fn, scope, args){
12946     var id = null, d, t;
12947
12948     var call = function(){
12949         var now = new Date().getTime();
12950         if(now - t >= d){
12951             clearInterval(id);
12952             id = null;
12953             fn.apply(scope, args || []);
12954         }
12955     };
12956     /**
12957      * Cancels any pending timeout and queues a new one
12958      * @param {Number} delay The milliseconds to delay
12959      * @param {Function} newFn (optional) Overrides function passed to constructor
12960      * @param {Object} newScope (optional) Overrides scope passed to constructor
12961      * @param {Array} newArgs (optional) Overrides args passed to constructor
12962      */
12963     this.delay = function(delay, newFn, newScope, newArgs){
12964         if(id && delay != d){
12965             this.cancel();
12966         }
12967         d = delay;
12968         t = new Date().getTime();
12969         fn = newFn || fn;
12970         scope = newScope || scope;
12971         args = newArgs || args;
12972         if(!id){
12973             id = setInterval(call, d);
12974         }
12975     };
12976
12977     /**
12978      * Cancel the last queued timeout
12979      */
12980     this.cancel = function(){
12981         if(id){
12982             clearInterval(id);
12983             id = null;
12984         }
12985     };
12986 };/*
12987  * Based on:
12988  * Ext JS Library 1.1.1
12989  * Copyright(c) 2006-2007, Ext JS, LLC.
12990  *
12991  * Originally Released Under LGPL - original licence link has changed is not relivant.
12992  *
12993  * Fork - LGPL
12994  * <script type="text/javascript">
12995  */
12996  
12997  
12998 Roo.util.TaskRunner = function(interval){
12999     interval = interval || 10;
13000     var tasks = [], removeQueue = [];
13001     var id = 0;
13002     var running = false;
13003
13004     var stopThread = function(){
13005         running = false;
13006         clearInterval(id);
13007         id = 0;
13008     };
13009
13010     var startThread = function(){
13011         if(!running){
13012             running = true;
13013             id = setInterval(runTasks, interval);
13014         }
13015     };
13016
13017     var removeTask = function(task){
13018         removeQueue.push(task);
13019         if(task.onStop){
13020             task.onStop();
13021         }
13022     };
13023
13024     var runTasks = function(){
13025         if(removeQueue.length > 0){
13026             for(var i = 0, len = removeQueue.length; i < len; i++){
13027                 tasks.remove(removeQueue[i]);
13028             }
13029             removeQueue = [];
13030             if(tasks.length < 1){
13031                 stopThread();
13032                 return;
13033             }
13034         }
13035         var now = new Date().getTime();
13036         for(var i = 0, len = tasks.length; i < len; ++i){
13037             var t = tasks[i];
13038             var itime = now - t.taskRunTime;
13039             if(t.interval <= itime){
13040                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13041                 t.taskRunTime = now;
13042                 if(rt === false || t.taskRunCount === t.repeat){
13043                     removeTask(t);
13044                     return;
13045                 }
13046             }
13047             if(t.duration && t.duration <= (now - t.taskStartTime)){
13048                 removeTask(t);
13049             }
13050         }
13051     };
13052
13053     /**
13054      * Queues a new task.
13055      * @param {Object} task
13056      */
13057     this.start = function(task){
13058         tasks.push(task);
13059         task.taskStartTime = new Date().getTime();
13060         task.taskRunTime = 0;
13061         task.taskRunCount = 0;
13062         startThread();
13063         return task;
13064     };
13065
13066     this.stop = function(task){
13067         removeTask(task);
13068         return task;
13069     };
13070
13071     this.stopAll = function(){
13072         stopThread();
13073         for(var i = 0, len = tasks.length; i < len; i++){
13074             if(tasks[i].onStop){
13075                 tasks[i].onStop();
13076             }
13077         }
13078         tasks = [];
13079         removeQueue = [];
13080     };
13081 };
13082
13083 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13084  * Based on:
13085  * Ext JS Library 1.1.1
13086  * Copyright(c) 2006-2007, Ext JS, LLC.
13087  *
13088  * Originally Released Under LGPL - original licence link has changed is not relivant.
13089  *
13090  * Fork - LGPL
13091  * <script type="text/javascript">
13092  */
13093
13094  
13095 /**
13096  * @class Roo.util.MixedCollection
13097  * @extends Roo.util.Observable
13098  * A Collection class that maintains both numeric indexes and keys and exposes events.
13099  * @constructor
13100  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13101  * collection (defaults to false)
13102  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13103  * and return the key value for that item.  This is used when available to look up the key on items that
13104  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13105  * equivalent to providing an implementation for the {@link #getKey} method.
13106  */
13107 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13108     this.items = [];
13109     this.map = {};
13110     this.keys = [];
13111     this.length = 0;
13112     this.addEvents({
13113         /**
13114          * @event clear
13115          * Fires when the collection is cleared.
13116          */
13117         "clear" : true,
13118         /**
13119          * @event add
13120          * Fires when an item is added to the collection.
13121          * @param {Number} index The index at which the item was added.
13122          * @param {Object} o The item added.
13123          * @param {String} key The key associated with the added item.
13124          */
13125         "add" : true,
13126         /**
13127          * @event replace
13128          * Fires when an item is replaced in the collection.
13129          * @param {String} key he key associated with the new added.
13130          * @param {Object} old The item being replaced.
13131          * @param {Object} new The new item.
13132          */
13133         "replace" : true,
13134         /**
13135          * @event remove
13136          * Fires when an item is removed from the collection.
13137          * @param {Object} o The item being removed.
13138          * @param {String} key (optional) The key associated with the removed item.
13139          */
13140         "remove" : true,
13141         "sort" : true
13142     });
13143     this.allowFunctions = allowFunctions === true;
13144     if(keyFn){
13145         this.getKey = keyFn;
13146     }
13147     Roo.util.MixedCollection.superclass.constructor.call(this);
13148 };
13149
13150 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13151     allowFunctions : false,
13152     
13153 /**
13154  * Adds an item to the collection.
13155  * @param {String} key The key to associate with the item
13156  * @param {Object} o The item to add.
13157  * @return {Object} The item added.
13158  */
13159     add : function(key, o){
13160         if(arguments.length == 1){
13161             o = arguments[0];
13162             key = this.getKey(o);
13163         }
13164         if(typeof key == "undefined" || key === null){
13165             this.length++;
13166             this.items.push(o);
13167             this.keys.push(null);
13168         }else{
13169             var old = this.map[key];
13170             if(old){
13171                 return this.replace(key, o);
13172             }
13173             this.length++;
13174             this.items.push(o);
13175             this.map[key] = o;
13176             this.keys.push(key);
13177         }
13178         this.fireEvent("add", this.length-1, o, key);
13179         return o;
13180     },
13181        
13182 /**
13183   * MixedCollection has a generic way to fetch keys if you implement getKey.
13184 <pre><code>
13185 // normal way
13186 var mc = new Roo.util.MixedCollection();
13187 mc.add(someEl.dom.id, someEl);
13188 mc.add(otherEl.dom.id, otherEl);
13189 //and so on
13190
13191 // using getKey
13192 var mc = new Roo.util.MixedCollection();
13193 mc.getKey = function(el){
13194    return el.dom.id;
13195 };
13196 mc.add(someEl);
13197 mc.add(otherEl);
13198
13199 // or via the constructor
13200 var mc = new Roo.util.MixedCollection(false, function(el){
13201    return el.dom.id;
13202 });
13203 mc.add(someEl);
13204 mc.add(otherEl);
13205 </code></pre>
13206  * @param o {Object} The item for which to find the key.
13207  * @return {Object} The key for the passed item.
13208  */
13209     getKey : function(o){
13210          return o.id; 
13211     },
13212    
13213 /**
13214  * Replaces an item in the collection.
13215  * @param {String} key The key associated with the item to replace, or the item to replace.
13216  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13217  * @return {Object}  The new item.
13218  */
13219     replace : function(key, o){
13220         if(arguments.length == 1){
13221             o = arguments[0];
13222             key = this.getKey(o);
13223         }
13224         var old = this.item(key);
13225         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13226              return this.add(key, o);
13227         }
13228         var index = this.indexOfKey(key);
13229         this.items[index] = o;
13230         this.map[key] = o;
13231         this.fireEvent("replace", key, old, o);
13232         return o;
13233     },
13234    
13235 /**
13236  * Adds all elements of an Array or an Object to the collection.
13237  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13238  * an Array of values, each of which are added to the collection.
13239  */
13240     addAll : function(objs){
13241         if(arguments.length > 1 || objs instanceof Array){
13242             var args = arguments.length > 1 ? arguments : objs;
13243             for(var i = 0, len = args.length; i < len; i++){
13244                 this.add(args[i]);
13245             }
13246         }else{
13247             for(var key in objs){
13248                 if(this.allowFunctions || typeof objs[key] != "function"){
13249                     this.add(key, objs[key]);
13250                 }
13251             }
13252         }
13253     },
13254    
13255 /**
13256  * Executes the specified function once for every item in the collection, passing each
13257  * item as the first and only parameter. returning false from the function will stop the iteration.
13258  * @param {Function} fn The function to execute for each item.
13259  * @param {Object} scope (optional) The scope in which to execute the function.
13260  */
13261     each : function(fn, scope){
13262         var items = [].concat(this.items); // each safe for removal
13263         for(var i = 0, len = items.length; i < len; i++){
13264             if(fn.call(scope || items[i], items[i], i, len) === false){
13265                 break;
13266             }
13267         }
13268     },
13269    
13270 /**
13271  * Executes the specified function once for every key in the collection, passing each
13272  * key, and its associated item as the first two parameters.
13273  * @param {Function} fn The function to execute for each item.
13274  * @param {Object} scope (optional) The scope in which to execute the function.
13275  */
13276     eachKey : function(fn, scope){
13277         for(var i = 0, len = this.keys.length; i < len; i++){
13278             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13279         }
13280     },
13281    
13282 /**
13283  * Returns the first item in the collection which elicits a true return value from the
13284  * passed selection function.
13285  * @param {Function} fn The selection function to execute for each item.
13286  * @param {Object} scope (optional) The scope in which to execute the function.
13287  * @return {Object} The first item in the collection which returned true from the selection function.
13288  */
13289     find : function(fn, scope){
13290         for(var i = 0, len = this.items.length; i < len; i++){
13291             if(fn.call(scope || window, this.items[i], this.keys[i])){
13292                 return this.items[i];
13293             }
13294         }
13295         return null;
13296     },
13297    
13298 /**
13299  * Inserts an item at the specified index in the collection.
13300  * @param {Number} index The index to insert the item at.
13301  * @param {String} key The key to associate with the new item, or the item itself.
13302  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13303  * @return {Object} The item inserted.
13304  */
13305     insert : function(index, key, o){
13306         if(arguments.length == 2){
13307             o = arguments[1];
13308             key = this.getKey(o);
13309         }
13310         if(index >= this.length){
13311             return this.add(key, o);
13312         }
13313         this.length++;
13314         this.items.splice(index, 0, o);
13315         if(typeof key != "undefined" && key != null){
13316             this.map[key] = o;
13317         }
13318         this.keys.splice(index, 0, key);
13319         this.fireEvent("add", index, o, key);
13320         return o;
13321     },
13322    
13323 /**
13324  * Removed an item from the collection.
13325  * @param {Object} o The item to remove.
13326  * @return {Object} The item removed.
13327  */
13328     remove : function(o){
13329         return this.removeAt(this.indexOf(o));
13330     },
13331    
13332 /**
13333  * Remove an item from a specified index in the collection.
13334  * @param {Number} index The index within the collection of the item to remove.
13335  */
13336     removeAt : function(index){
13337         if(index < this.length && index >= 0){
13338             this.length--;
13339             var o = this.items[index];
13340             this.items.splice(index, 1);
13341             var key = this.keys[index];
13342             if(typeof key != "undefined"){
13343                 delete this.map[key];
13344             }
13345             this.keys.splice(index, 1);
13346             this.fireEvent("remove", o, key);
13347         }
13348     },
13349    
13350 /**
13351  * Removed an item associated with the passed key fom the collection.
13352  * @param {String} key The key of the item to remove.
13353  */
13354     removeKey : function(key){
13355         return this.removeAt(this.indexOfKey(key));
13356     },
13357    
13358 /**
13359  * Returns the number of items in the collection.
13360  * @return {Number} the number of items in the collection.
13361  */
13362     getCount : function(){
13363         return this.length; 
13364     },
13365    
13366 /**
13367  * Returns index within the collection of the passed Object.
13368  * @param {Object} o The item to find the index of.
13369  * @return {Number} index of the item.
13370  */
13371     indexOf : function(o){
13372         if(!this.items.indexOf){
13373             for(var i = 0, len = this.items.length; i < len; i++){
13374                 if(this.items[i] == o) {
13375                     return i;
13376                 }
13377             }
13378             return -1;
13379         }else{
13380             return this.items.indexOf(o);
13381         }
13382     },
13383    
13384 /**
13385  * Returns index within the collection of the passed key.
13386  * @param {String} key The key to find the index of.
13387  * @return {Number} index of the key.
13388  */
13389     indexOfKey : function(key){
13390         if(!this.keys.indexOf){
13391             for(var i = 0, len = this.keys.length; i < len; i++){
13392                 if(this.keys[i] == key) {
13393                     return i;
13394                 }
13395             }
13396             return -1;
13397         }else{
13398             return this.keys.indexOf(key);
13399         }
13400     },
13401    
13402 /**
13403  * Returns the item associated with the passed key OR index. Key has priority over index.
13404  * @param {String/Number} key The key or index of the item.
13405  * @return {Object} The item associated with the passed key.
13406  */
13407     item : function(key){
13408         if (key === 'length') {
13409             return null;
13410         }
13411         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13412         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13413     },
13414     
13415 /**
13416  * Returns the item at the specified index.
13417  * @param {Number} index The index of the item.
13418  * @return {Object}
13419  */
13420     itemAt : function(index){
13421         return this.items[index];
13422     },
13423     
13424 /**
13425  * Returns the item associated with the passed key.
13426  * @param {String/Number} key The key of the item.
13427  * @return {Object} The item associated with the passed key.
13428  */
13429     key : function(key){
13430         return this.map[key];
13431     },
13432    
13433 /**
13434  * Returns true if the collection contains the passed Object as an item.
13435  * @param {Object} o  The Object to look for in the collection.
13436  * @return {Boolean} True if the collection contains the Object as an item.
13437  */
13438     contains : function(o){
13439         return this.indexOf(o) != -1;
13440     },
13441    
13442 /**
13443  * Returns true if the collection contains the passed Object as a key.
13444  * @param {String} key The key to look for in the collection.
13445  * @return {Boolean} True if the collection contains the Object as a key.
13446  */
13447     containsKey : function(key){
13448         return typeof this.map[key] != "undefined";
13449     },
13450    
13451 /**
13452  * Removes all items from the collection.
13453  */
13454     clear : function(){
13455         this.length = 0;
13456         this.items = [];
13457         this.keys = [];
13458         this.map = {};
13459         this.fireEvent("clear");
13460     },
13461    
13462 /**
13463  * Returns the first item in the collection.
13464  * @return {Object} the first item in the collection..
13465  */
13466     first : function(){
13467         return this.items[0]; 
13468     },
13469    
13470 /**
13471  * Returns the last item in the collection.
13472  * @return {Object} the last item in the collection..
13473  */
13474     last : function(){
13475         return this.items[this.length-1];   
13476     },
13477     
13478     _sort : function(property, dir, fn){
13479         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13480         fn = fn || function(a, b){
13481             return a-b;
13482         };
13483         var c = [], k = this.keys, items = this.items;
13484         for(var i = 0, len = items.length; i < len; i++){
13485             c[c.length] = {key: k[i], value: items[i], index: i};
13486         }
13487         c.sort(function(a, b){
13488             var v = fn(a[property], b[property]) * dsc;
13489             if(v == 0){
13490                 v = (a.index < b.index ? -1 : 1);
13491             }
13492             return v;
13493         });
13494         for(var i = 0, len = c.length; i < len; i++){
13495             items[i] = c[i].value;
13496             k[i] = c[i].key;
13497         }
13498         this.fireEvent("sort", this);
13499     },
13500     
13501     /**
13502      * Sorts this collection with the passed comparison function
13503      * @param {String} direction (optional) "ASC" or "DESC"
13504      * @param {Function} fn (optional) comparison function
13505      */
13506     sort : function(dir, fn){
13507         this._sort("value", dir, fn);
13508     },
13509     
13510     /**
13511      * Sorts this collection by keys
13512      * @param {String} direction (optional) "ASC" or "DESC"
13513      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13514      */
13515     keySort : function(dir, fn){
13516         this._sort("key", dir, fn || function(a, b){
13517             return String(a).toUpperCase()-String(b).toUpperCase();
13518         });
13519     },
13520     
13521     /**
13522      * Returns a range of items in this collection
13523      * @param {Number} startIndex (optional) defaults to 0
13524      * @param {Number} endIndex (optional) default to the last item
13525      * @return {Array} An array of items
13526      */
13527     getRange : function(start, end){
13528         var items = this.items;
13529         if(items.length < 1){
13530             return [];
13531         }
13532         start = start || 0;
13533         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13534         var r = [];
13535         if(start <= end){
13536             for(var i = start; i <= end; i++) {
13537                     r[r.length] = items[i];
13538             }
13539         }else{
13540             for(var i = start; i >= end; i--) {
13541                     r[r.length] = items[i];
13542             }
13543         }
13544         return r;
13545     },
13546         
13547     /**
13548      * Filter the <i>objects</i> in this collection by a specific property. 
13549      * Returns a new collection that has been filtered.
13550      * @param {String} property A property on your objects
13551      * @param {String/RegExp} value Either string that the property values 
13552      * should start with or a RegExp to test against the property
13553      * @return {MixedCollection} The new filtered collection
13554      */
13555     filter : function(property, value){
13556         if(!value.exec){ // not a regex
13557             value = String(value);
13558             if(value.length == 0){
13559                 return this.clone();
13560             }
13561             value = new RegExp("^" + Roo.escapeRe(value), "i");
13562         }
13563         return this.filterBy(function(o){
13564             return o && value.test(o[property]);
13565         });
13566         },
13567     
13568     /**
13569      * Filter by a function. * Returns a new collection that has been filtered.
13570      * The passed function will be called with each 
13571      * object in the collection. If the function returns true, the value is included 
13572      * otherwise it is filtered.
13573      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13574      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13575      * @return {MixedCollection} The new filtered collection
13576      */
13577     filterBy : function(fn, scope){
13578         var r = new Roo.util.MixedCollection();
13579         r.getKey = this.getKey;
13580         var k = this.keys, it = this.items;
13581         for(var i = 0, len = it.length; i < len; i++){
13582             if(fn.call(scope||this, it[i], k[i])){
13583                                 r.add(k[i], it[i]);
13584                         }
13585         }
13586         return r;
13587     },
13588     
13589     /**
13590      * Creates a duplicate of this collection
13591      * @return {MixedCollection}
13592      */
13593     clone : function(){
13594         var r = new Roo.util.MixedCollection();
13595         var k = this.keys, it = this.items;
13596         for(var i = 0, len = it.length; i < len; i++){
13597             r.add(k[i], it[i]);
13598         }
13599         r.getKey = this.getKey;
13600         return r;
13601     }
13602 });
13603 /**
13604  * Returns the item associated with the passed key or index.
13605  * @method
13606  * @param {String/Number} key The key or index of the item.
13607  * @return {Object} The item associated with the passed key.
13608  */
13609 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13610  * Based on:
13611  * Ext JS Library 1.1.1
13612  * Copyright(c) 2006-2007, Ext JS, LLC.
13613  *
13614  * Originally Released Under LGPL - original licence link has changed is not relivant.
13615  *
13616  * Fork - LGPL
13617  * <script type="text/javascript">
13618  */
13619 /**
13620  * @class Roo.util.JSON
13621  * Modified version of Douglas Crockford"s json.js that doesn"t
13622  * mess with the Object prototype 
13623  * http://www.json.org/js.html
13624  * @singleton
13625  */
13626 Roo.util.JSON = new (function(){
13627     var useHasOwn = {}.hasOwnProperty ? true : false;
13628     
13629     // crashes Safari in some instances
13630     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13631     
13632     var pad = function(n) {
13633         return n < 10 ? "0" + n : n;
13634     };
13635     
13636     var m = {
13637         "\b": '\\b',
13638         "\t": '\\t',
13639         "\n": '\\n',
13640         "\f": '\\f',
13641         "\r": '\\r',
13642         '"' : '\\"',
13643         "\\": '\\\\'
13644     };
13645
13646     var encodeString = function(s){
13647         if (/["\\\x00-\x1f]/.test(s)) {
13648             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13649                 var c = m[b];
13650                 if(c){
13651                     return c;
13652                 }
13653                 c = b.charCodeAt();
13654                 return "\\u00" +
13655                     Math.floor(c / 16).toString(16) +
13656                     (c % 16).toString(16);
13657             }) + '"';
13658         }
13659         return '"' + s + '"';
13660     };
13661     
13662     var encodeArray = function(o){
13663         var a = ["["], b, i, l = o.length, v;
13664             for (i = 0; i < l; i += 1) {
13665                 v = o[i];
13666                 switch (typeof v) {
13667                     case "undefined":
13668                     case "function":
13669                     case "unknown":
13670                         break;
13671                     default:
13672                         if (b) {
13673                             a.push(',');
13674                         }
13675                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13676                         b = true;
13677                 }
13678             }
13679             a.push("]");
13680             return a.join("");
13681     };
13682     
13683     var encodeDate = function(o){
13684         return '"' + o.getFullYear() + "-" +
13685                 pad(o.getMonth() + 1) + "-" +
13686                 pad(o.getDate()) + "T" +
13687                 pad(o.getHours()) + ":" +
13688                 pad(o.getMinutes()) + ":" +
13689                 pad(o.getSeconds()) + '"';
13690     };
13691     
13692     /**
13693      * Encodes an Object, Array or other value
13694      * @param {Mixed} o The variable to encode
13695      * @return {String} The JSON string
13696      */
13697     this.encode = function(o)
13698     {
13699         // should this be extended to fully wrap stringify..
13700         
13701         if(typeof o == "undefined" || o === null){
13702             return "null";
13703         }else if(o instanceof Array){
13704             return encodeArray(o);
13705         }else if(o instanceof Date){
13706             return encodeDate(o);
13707         }else if(typeof o == "string"){
13708             return encodeString(o);
13709         }else if(typeof o == "number"){
13710             return isFinite(o) ? String(o) : "null";
13711         }else if(typeof o == "boolean"){
13712             return String(o);
13713         }else {
13714             var a = ["{"], b, i, v;
13715             for (i in o) {
13716                 if(!useHasOwn || o.hasOwnProperty(i)) {
13717                     v = o[i];
13718                     switch (typeof v) {
13719                     case "undefined":
13720                     case "function":
13721                     case "unknown":
13722                         break;
13723                     default:
13724                         if(b){
13725                             a.push(',');
13726                         }
13727                         a.push(this.encode(i), ":",
13728                                 v === null ? "null" : this.encode(v));
13729                         b = true;
13730                     }
13731                 }
13732             }
13733             a.push("}");
13734             return a.join("");
13735         }
13736     };
13737     
13738     /**
13739      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13740      * @param {String} json The JSON string
13741      * @return {Object} The resulting object
13742      */
13743     this.decode = function(json){
13744         
13745         return  /** eval:var:json */ eval("(" + json + ')');
13746     };
13747 })();
13748 /** 
13749  * Shorthand for {@link Roo.util.JSON#encode}
13750  * @member Roo encode 
13751  * @method */
13752 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13753 /** 
13754  * Shorthand for {@link Roo.util.JSON#decode}
13755  * @member Roo decode 
13756  * @method */
13757 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13758 /*
13759  * Based on:
13760  * Ext JS Library 1.1.1
13761  * Copyright(c) 2006-2007, Ext JS, LLC.
13762  *
13763  * Originally Released Under LGPL - original licence link has changed is not relivant.
13764  *
13765  * Fork - LGPL
13766  * <script type="text/javascript">
13767  */
13768  
13769 /**
13770  * @class Roo.util.Format
13771  * Reusable data formatting functions
13772  * @singleton
13773  */
13774 Roo.util.Format = function(){
13775     var trimRe = /^\s+|\s+$/g;
13776     return {
13777         /**
13778          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13779          * @param {String} value The string to truncate
13780          * @param {Number} length The maximum length to allow before truncating
13781          * @return {String} The converted text
13782          */
13783         ellipsis : function(value, len){
13784             if(value && value.length > len){
13785                 return value.substr(0, len-3)+"...";
13786             }
13787             return value;
13788         },
13789
13790         /**
13791          * Checks a reference and converts it to empty string if it is undefined
13792          * @param {Mixed} value Reference to check
13793          * @return {Mixed} Empty string if converted, otherwise the original value
13794          */
13795         undef : function(value){
13796             return typeof value != "undefined" ? value : "";
13797         },
13798
13799         /**
13800          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13801          * @param {String} value The string to encode
13802          * @return {String} The encoded text
13803          */
13804         htmlEncode : function(value){
13805             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13806         },
13807
13808         /**
13809          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13810          * @param {String} value The string to decode
13811          * @return {String} The decoded text
13812          */
13813         htmlDecode : function(value){
13814             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13815         },
13816
13817         /**
13818          * Trims any whitespace from either side of a string
13819          * @param {String} value The text to trim
13820          * @return {String} The trimmed text
13821          */
13822         trim : function(value){
13823             return String(value).replace(trimRe, "");
13824         },
13825
13826         /**
13827          * Returns a substring from within an original string
13828          * @param {String} value The original text
13829          * @param {Number} start The start index of the substring
13830          * @param {Number} length The length of the substring
13831          * @return {String} The substring
13832          */
13833         substr : function(value, start, length){
13834             return String(value).substr(start, length);
13835         },
13836
13837         /**
13838          * Converts a string to all lower case letters
13839          * @param {String} value The text to convert
13840          * @return {String} The converted text
13841          */
13842         lowercase : function(value){
13843             return String(value).toLowerCase();
13844         },
13845
13846         /**
13847          * Converts a string to all upper case letters
13848          * @param {String} value The text to convert
13849          * @return {String} The converted text
13850          */
13851         uppercase : function(value){
13852             return String(value).toUpperCase();
13853         },
13854
13855         /**
13856          * Converts the first character only of a string to upper case
13857          * @param {String} value The text to convert
13858          * @return {String} The converted text
13859          */
13860         capitalize : function(value){
13861             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13862         },
13863
13864         // private
13865         call : function(value, fn){
13866             if(arguments.length > 2){
13867                 var args = Array.prototype.slice.call(arguments, 2);
13868                 args.unshift(value);
13869                  
13870                 return /** eval:var:value */  eval(fn).apply(window, args);
13871             }else{
13872                 /** eval:var:value */
13873                 return /** eval:var:value */ eval(fn).call(window, value);
13874             }
13875         },
13876
13877        
13878         /**
13879          * safer version of Math.toFixed..??/
13880          * @param {Number/String} value The numeric value to format
13881          * @param {Number/String} value Decimal places 
13882          * @return {String} The formatted currency string
13883          */
13884         toFixed : function(v, n)
13885         {
13886             // why not use to fixed - precision is buggered???
13887             if (!n) {
13888                 return Math.round(v-0);
13889             }
13890             var fact = Math.pow(10,n+1);
13891             v = (Math.round((v-0)*fact))/fact;
13892             var z = (''+fact).substring(2);
13893             if (v == Math.floor(v)) {
13894                 return Math.floor(v) + '.' + z;
13895             }
13896             
13897             // now just padd decimals..
13898             var ps = String(v).split('.');
13899             var fd = (ps[1] + z);
13900             var r = fd.substring(0,n); 
13901             var rm = fd.substring(n); 
13902             if (rm < 5) {
13903                 return ps[0] + '.' + r;
13904             }
13905             r*=1; // turn it into a number;
13906             r++;
13907             if (String(r).length != n) {
13908                 ps[0]*=1;
13909                 ps[0]++;
13910                 r = String(r).substring(1); // chop the end off.
13911             }
13912             
13913             return ps[0] + '.' + r;
13914              
13915         },
13916         
13917         /**
13918          * Format a number as US currency
13919          * @param {Number/String} value The numeric value to format
13920          * @return {String} The formatted currency string
13921          */
13922         usMoney : function(v){
13923             return '$' + Roo.util.Format.number(v);
13924         },
13925         
13926         /**
13927          * Format a number
13928          * eventually this should probably emulate php's number_format
13929          * @param {Number/String} value The numeric value to format
13930          * @param {Number} decimals number of decimal places
13931          * @param {String} delimiter for thousands (default comma)
13932          * @return {String} The formatted currency string
13933          */
13934         number : function(v, decimals, thousandsDelimiter)
13935         {
13936             // multiply and round.
13937             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13938             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13939             
13940             var mul = Math.pow(10, decimals);
13941             var zero = String(mul).substring(1);
13942             v = (Math.round((v-0)*mul))/mul;
13943             
13944             // if it's '0' number.. then
13945             
13946             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13947             v = String(v);
13948             var ps = v.split('.');
13949             var whole = ps[0];
13950             
13951             var r = /(\d+)(\d{3})/;
13952             // add comma's
13953             
13954             if(thousandsDelimiter.length != 0) {
13955                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
13956             } 
13957             
13958             var sub = ps[1] ?
13959                     // has decimals..
13960                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13961                     // does not have decimals
13962                     (decimals ? ('.' + zero) : '');
13963             
13964             
13965             return whole + sub ;
13966         },
13967         
13968         /**
13969          * Parse a value into a formatted date using the specified format pattern.
13970          * @param {Mixed} value The value to format
13971          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13972          * @return {String} The formatted date string
13973          */
13974         date : function(v, format){
13975             if(!v){
13976                 return "";
13977             }
13978             if(!(v instanceof Date)){
13979                 v = new Date(Date.parse(v));
13980             }
13981             return v.dateFormat(format || Roo.util.Format.defaults.date);
13982         },
13983
13984         /**
13985          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13986          * @param {String} format Any valid date format string
13987          * @return {Function} The date formatting function
13988          */
13989         dateRenderer : function(format){
13990             return function(v){
13991                 return Roo.util.Format.date(v, format);  
13992             };
13993         },
13994
13995         // private
13996         stripTagsRE : /<\/?[^>]+>/gi,
13997         
13998         /**
13999          * Strips all HTML tags
14000          * @param {Mixed} value The text from which to strip tags
14001          * @return {String} The stripped text
14002          */
14003         stripTags : function(v){
14004             return !v ? v : String(v).replace(this.stripTagsRE, "");
14005         },
14006         
14007         /**
14008          * Size in Mb,Gb etc.
14009          * @param {Number} value The number to be formated
14010          * @param {number} decimals how many decimal places
14011          * @return {String} the formated string
14012          */
14013         size : function(value, decimals)
14014         {
14015             var sizes = ['b', 'k', 'M', 'G', 'T'];
14016             if (value == 0) {
14017                 return 0;
14018             }
14019             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14020             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14021         }
14022         
14023         
14024         
14025     };
14026 }();
14027 Roo.util.Format.defaults = {
14028     date : 'd/M/Y'
14029 };/*
14030  * Based on:
14031  * Ext JS Library 1.1.1
14032  * Copyright(c) 2006-2007, Ext JS, LLC.
14033  *
14034  * Originally Released Under LGPL - original licence link has changed is not relivant.
14035  *
14036  * Fork - LGPL
14037  * <script type="text/javascript">
14038  */
14039
14040
14041  
14042
14043 /**
14044  * @class Roo.MasterTemplate
14045  * @extends Roo.Template
14046  * Provides a template that can have child templates. The syntax is:
14047 <pre><code>
14048 var t = new Roo.MasterTemplate(
14049         '&lt;select name="{name}"&gt;',
14050                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14051         '&lt;/select&gt;'
14052 );
14053 t.add('options', {value: 'foo', text: 'bar'});
14054 // or you can add multiple child elements in one shot
14055 t.addAll('options', [
14056     {value: 'foo', text: 'bar'},
14057     {value: 'foo2', text: 'bar2'},
14058     {value: 'foo3', text: 'bar3'}
14059 ]);
14060 // then append, applying the master template values
14061 t.append('my-form', {name: 'my-select'});
14062 </code></pre>
14063 * A name attribute for the child template is not required if you have only one child
14064 * template or you want to refer to them by index.
14065  */
14066 Roo.MasterTemplate = function(){
14067     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14068     this.originalHtml = this.html;
14069     var st = {};
14070     var m, re = this.subTemplateRe;
14071     re.lastIndex = 0;
14072     var subIndex = 0;
14073     while(m = re.exec(this.html)){
14074         var name = m[1], content = m[2];
14075         st[subIndex] = {
14076             name: name,
14077             index: subIndex,
14078             buffer: [],
14079             tpl : new Roo.Template(content)
14080         };
14081         if(name){
14082             st[name] = st[subIndex];
14083         }
14084         st[subIndex].tpl.compile();
14085         st[subIndex].tpl.call = this.call.createDelegate(this);
14086         subIndex++;
14087     }
14088     this.subCount = subIndex;
14089     this.subs = st;
14090 };
14091 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14092     /**
14093     * The regular expression used to match sub templates
14094     * @type RegExp
14095     * @property
14096     */
14097     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14098
14099     /**
14100      * Applies the passed values to a child template.
14101      * @param {String/Number} name (optional) The name or index of the child template
14102      * @param {Array/Object} values The values to be applied to the template
14103      * @return {MasterTemplate} this
14104      */
14105      add : function(name, values){
14106         if(arguments.length == 1){
14107             values = arguments[0];
14108             name = 0;
14109         }
14110         var s = this.subs[name];
14111         s.buffer[s.buffer.length] = s.tpl.apply(values);
14112         return this;
14113     },
14114
14115     /**
14116      * Applies all the passed values to a child template.
14117      * @param {String/Number} name (optional) The name or index of the child template
14118      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14119      * @param {Boolean} reset (optional) True to reset the template first
14120      * @return {MasterTemplate} this
14121      */
14122     fill : function(name, values, reset){
14123         var a = arguments;
14124         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14125             values = a[0];
14126             name = 0;
14127             reset = a[1];
14128         }
14129         if(reset){
14130             this.reset();
14131         }
14132         for(var i = 0, len = values.length; i < len; i++){
14133             this.add(name, values[i]);
14134         }
14135         return this;
14136     },
14137
14138     /**
14139      * Resets the template for reuse
14140      * @return {MasterTemplate} this
14141      */
14142      reset : function(){
14143         var s = this.subs;
14144         for(var i = 0; i < this.subCount; i++){
14145             s[i].buffer = [];
14146         }
14147         return this;
14148     },
14149
14150     applyTemplate : function(values){
14151         var s = this.subs;
14152         var replaceIndex = -1;
14153         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14154             return s[++replaceIndex].buffer.join("");
14155         });
14156         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14157     },
14158
14159     apply : function(){
14160         return this.applyTemplate.apply(this, arguments);
14161     },
14162
14163     compile : function(){return this;}
14164 });
14165
14166 /**
14167  * Alias for fill().
14168  * @method
14169  */
14170 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14171  /**
14172  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14173  * var tpl = Roo.MasterTemplate.from('element-id');
14174  * @param {String/HTMLElement} el
14175  * @param {Object} config
14176  * @static
14177  */
14178 Roo.MasterTemplate.from = function(el, config){
14179     el = Roo.getDom(el);
14180     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14181 };/*
14182  * Based on:
14183  * Ext JS Library 1.1.1
14184  * Copyright(c) 2006-2007, Ext JS, LLC.
14185  *
14186  * Originally Released Under LGPL - original licence link has changed is not relivant.
14187  *
14188  * Fork - LGPL
14189  * <script type="text/javascript">
14190  */
14191
14192  
14193 /**
14194  * @class Roo.util.CSS
14195  * Utility class for manipulating CSS rules
14196  * @singleton
14197  */
14198 Roo.util.CSS = function(){
14199         var rules = null;
14200         var doc = document;
14201
14202     var camelRe = /(-[a-z])/gi;
14203     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14204
14205    return {
14206    /**
14207     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14208     * tag and appended to the HEAD of the document.
14209     * @param {String|Object} cssText The text containing the css rules
14210     * @param {String} id An id to add to the stylesheet for later removal
14211     * @return {StyleSheet}
14212     */
14213     createStyleSheet : function(cssText, id){
14214         var ss;
14215         var head = doc.getElementsByTagName("head")[0];
14216         var nrules = doc.createElement("style");
14217         nrules.setAttribute("type", "text/css");
14218         if(id){
14219             nrules.setAttribute("id", id);
14220         }
14221         if (typeof(cssText) != 'string') {
14222             // support object maps..
14223             // not sure if this a good idea.. 
14224             // perhaps it should be merged with the general css handling
14225             // and handle js style props.
14226             var cssTextNew = [];
14227             for(var n in cssText) {
14228                 var citems = [];
14229                 for(var k in cssText[n]) {
14230                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14231                 }
14232                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14233                 
14234             }
14235             cssText = cssTextNew.join("\n");
14236             
14237         }
14238        
14239        
14240        if(Roo.isIE){
14241            head.appendChild(nrules);
14242            ss = nrules.styleSheet;
14243            ss.cssText = cssText;
14244        }else{
14245            try{
14246                 nrules.appendChild(doc.createTextNode(cssText));
14247            }catch(e){
14248                nrules.cssText = cssText; 
14249            }
14250            head.appendChild(nrules);
14251            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14252        }
14253        this.cacheStyleSheet(ss);
14254        return ss;
14255    },
14256
14257    /**
14258     * Removes a style or link tag by id
14259     * @param {String} id The id of the tag
14260     */
14261    removeStyleSheet : function(id){
14262        var existing = doc.getElementById(id);
14263        if(existing){
14264            existing.parentNode.removeChild(existing);
14265        }
14266    },
14267
14268    /**
14269     * Dynamically swaps an existing stylesheet reference for a new one
14270     * @param {String} id The id of an existing link tag to remove
14271     * @param {String} url The href of the new stylesheet to include
14272     */
14273    swapStyleSheet : function(id, url){
14274        this.removeStyleSheet(id);
14275        var ss = doc.createElement("link");
14276        ss.setAttribute("rel", "stylesheet");
14277        ss.setAttribute("type", "text/css");
14278        ss.setAttribute("id", id);
14279        ss.setAttribute("href", url);
14280        doc.getElementsByTagName("head")[0].appendChild(ss);
14281    },
14282    
14283    /**
14284     * Refresh the rule cache if you have dynamically added stylesheets
14285     * @return {Object} An object (hash) of rules indexed by selector
14286     */
14287    refreshCache : function(){
14288        return this.getRules(true);
14289    },
14290
14291    // private
14292    cacheStyleSheet : function(stylesheet){
14293        if(!rules){
14294            rules = {};
14295        }
14296        try{// try catch for cross domain access issue
14297            var ssRules = stylesheet.cssRules || stylesheet.rules;
14298            for(var j = ssRules.length-1; j >= 0; --j){
14299                rules[ssRules[j].selectorText] = ssRules[j];
14300            }
14301        }catch(e){}
14302    },
14303    
14304    /**
14305     * Gets all css rules for the document
14306     * @param {Boolean} refreshCache true to refresh the internal cache
14307     * @return {Object} An object (hash) of rules indexed by selector
14308     */
14309    getRules : function(refreshCache){
14310                 if(rules == null || refreshCache){
14311                         rules = {};
14312                         var ds = doc.styleSheets;
14313                         for(var i =0, len = ds.length; i < len; i++){
14314                             try{
14315                         this.cacheStyleSheet(ds[i]);
14316                     }catch(e){} 
14317                 }
14318                 }
14319                 return rules;
14320         },
14321         
14322         /**
14323     * Gets an an individual CSS rule by selector(s)
14324     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14325     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14326     * @return {CSSRule} The CSS rule or null if one is not found
14327     */
14328    getRule : function(selector, refreshCache){
14329                 var rs = this.getRules(refreshCache);
14330                 if(!(selector instanceof Array)){
14331                     return rs[selector];
14332                 }
14333                 for(var i = 0; i < selector.length; i++){
14334                         if(rs[selector[i]]){
14335                                 return rs[selector[i]];
14336                         }
14337                 }
14338                 return null;
14339         },
14340         
14341         
14342         /**
14343     * Updates a rule property
14344     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14345     * @param {String} property The css property
14346     * @param {String} value The new value for the property
14347     * @return {Boolean} true If a rule was found and updated
14348     */
14349    updateRule : function(selector, property, value){
14350                 if(!(selector instanceof Array)){
14351                         var rule = this.getRule(selector);
14352                         if(rule){
14353                                 rule.style[property.replace(camelRe, camelFn)] = value;
14354                                 return true;
14355                         }
14356                 }else{
14357                         for(var i = 0; i < selector.length; i++){
14358                                 if(this.updateRule(selector[i], property, value)){
14359                                         return true;
14360                                 }
14361                         }
14362                 }
14363                 return false;
14364         }
14365    };   
14366 }();/*
14367  * Based on:
14368  * Ext JS Library 1.1.1
14369  * Copyright(c) 2006-2007, Ext JS, LLC.
14370  *
14371  * Originally Released Under LGPL - original licence link has changed is not relivant.
14372  *
14373  * Fork - LGPL
14374  * <script type="text/javascript">
14375  */
14376
14377  
14378
14379 /**
14380  * @class Roo.util.ClickRepeater
14381  * @extends Roo.util.Observable
14382  * 
14383  * A wrapper class which can be applied to any element. Fires a "click" event while the
14384  * mouse is pressed. The interval between firings may be specified in the config but
14385  * defaults to 10 milliseconds.
14386  * 
14387  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14388  * 
14389  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14390  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14391  * Similar to an autorepeat key delay.
14392  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14393  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14394  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14395  *           "interval" and "delay" are ignored. "immediate" is honored.
14396  * @cfg {Boolean} preventDefault True to prevent the default click event
14397  * @cfg {Boolean} stopDefault True to stop the default click event
14398  * 
14399  * @history
14400  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14401  *     2007-02-02 jvs Renamed to ClickRepeater
14402  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14403  *
14404  *  @constructor
14405  * @param {String/HTMLElement/Element} el The element to listen on
14406  * @param {Object} config
14407  **/
14408 Roo.util.ClickRepeater = function(el, config)
14409 {
14410     this.el = Roo.get(el);
14411     this.el.unselectable();
14412
14413     Roo.apply(this, config);
14414
14415     this.addEvents({
14416     /**
14417      * @event mousedown
14418      * Fires when the mouse button is depressed.
14419      * @param {Roo.util.ClickRepeater} this
14420      */
14421         "mousedown" : true,
14422     /**
14423      * @event click
14424      * Fires on a specified interval during the time the element is pressed.
14425      * @param {Roo.util.ClickRepeater} this
14426      */
14427         "click" : true,
14428     /**
14429      * @event mouseup
14430      * Fires when the mouse key is released.
14431      * @param {Roo.util.ClickRepeater} this
14432      */
14433         "mouseup" : true
14434     });
14435
14436     this.el.on("mousedown", this.handleMouseDown, this);
14437     if(this.preventDefault || this.stopDefault){
14438         this.el.on("click", function(e){
14439             if(this.preventDefault){
14440                 e.preventDefault();
14441             }
14442             if(this.stopDefault){
14443                 e.stopEvent();
14444             }
14445         }, this);
14446     }
14447
14448     // allow inline handler
14449     if(this.handler){
14450         this.on("click", this.handler,  this.scope || this);
14451     }
14452
14453     Roo.util.ClickRepeater.superclass.constructor.call(this);
14454 };
14455
14456 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14457     interval : 20,
14458     delay: 250,
14459     preventDefault : true,
14460     stopDefault : false,
14461     timer : 0,
14462
14463     // private
14464     handleMouseDown : function(){
14465         clearTimeout(this.timer);
14466         this.el.blur();
14467         if(this.pressClass){
14468             this.el.addClass(this.pressClass);
14469         }
14470         this.mousedownTime = new Date();
14471
14472         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14473         this.el.on("mouseout", this.handleMouseOut, this);
14474
14475         this.fireEvent("mousedown", this);
14476         this.fireEvent("click", this);
14477         
14478         this.timer = this.click.defer(this.delay || this.interval, this);
14479     },
14480
14481     // private
14482     click : function(){
14483         this.fireEvent("click", this);
14484         this.timer = this.click.defer(this.getInterval(), this);
14485     },
14486
14487     // private
14488     getInterval: function(){
14489         if(!this.accelerate){
14490             return this.interval;
14491         }
14492         var pressTime = this.mousedownTime.getElapsed();
14493         if(pressTime < 500){
14494             return 400;
14495         }else if(pressTime < 1700){
14496             return 320;
14497         }else if(pressTime < 2600){
14498             return 250;
14499         }else if(pressTime < 3500){
14500             return 180;
14501         }else if(pressTime < 4400){
14502             return 140;
14503         }else if(pressTime < 5300){
14504             return 80;
14505         }else if(pressTime < 6200){
14506             return 50;
14507         }else{
14508             return 10;
14509         }
14510     },
14511
14512     // private
14513     handleMouseOut : function(){
14514         clearTimeout(this.timer);
14515         if(this.pressClass){
14516             this.el.removeClass(this.pressClass);
14517         }
14518         this.el.on("mouseover", this.handleMouseReturn, this);
14519     },
14520
14521     // private
14522     handleMouseReturn : function(){
14523         this.el.un("mouseover", this.handleMouseReturn);
14524         if(this.pressClass){
14525             this.el.addClass(this.pressClass);
14526         }
14527         this.click();
14528     },
14529
14530     // private
14531     handleMouseUp : function(){
14532         clearTimeout(this.timer);
14533         this.el.un("mouseover", this.handleMouseReturn);
14534         this.el.un("mouseout", this.handleMouseOut);
14535         Roo.get(document).un("mouseup", this.handleMouseUp);
14536         this.el.removeClass(this.pressClass);
14537         this.fireEvent("mouseup", this);
14538     }
14539 });/**
14540  * @class Roo.util.Clipboard
14541  * @static
14542  * 
14543  * Clipboard UTILS
14544  * 
14545  **/
14546 Roo.util.Clipboard = {
14547     /**
14548      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
14549      * @param {String} text to copy to clipboard
14550      */
14551     write : function(text) {
14552         // navigator clipboard api needs a secure context (https)
14553         if (navigator.clipboard && window.isSecureContext) {
14554             // navigator clipboard api method'
14555             navigator.clipboard.writeText(text);
14556             return ;
14557         } 
14558         // text area method
14559         var ta = document.createElement("textarea");
14560         ta.value = text;
14561         // make the textarea out of viewport
14562         ta.style.position = "fixed";
14563         ta.style.left = "-999999px";
14564         ta.style.top = "-999999px";
14565         document.body.appendChild(ta);
14566         ta.focus();
14567         ta.select();
14568         document.execCommand('copy');
14569         (function() {
14570             ta.remove();
14571         }).defer(100);
14572         
14573     }
14574         
14575 }
14576     /*
14577  * Based on:
14578  * Ext JS Library 1.1.1
14579  * Copyright(c) 2006-2007, Ext JS, LLC.
14580  *
14581  * Originally Released Under LGPL - original licence link has changed is not relivant.
14582  *
14583  * Fork - LGPL
14584  * <script type="text/javascript">
14585  */
14586
14587  
14588 /**
14589  * @class Roo.KeyNav
14590  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14591  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14592  * way to implement custom navigation schemes for any UI component.</p>
14593  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14594  * pageUp, pageDown, del, home, end.  Usage:</p>
14595  <pre><code>
14596 var nav = new Roo.KeyNav("my-element", {
14597     "left" : function(e){
14598         this.moveLeft(e.ctrlKey);
14599     },
14600     "right" : function(e){
14601         this.moveRight(e.ctrlKey);
14602     },
14603     "enter" : function(e){
14604         this.save();
14605     },
14606     scope : this
14607 });
14608 </code></pre>
14609  * @constructor
14610  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14611  * @param {Object} config The config
14612  */
14613 Roo.KeyNav = function(el, config){
14614     this.el = Roo.get(el);
14615     Roo.apply(this, config);
14616     if(!this.disabled){
14617         this.disabled = true;
14618         this.enable();
14619     }
14620 };
14621
14622 Roo.KeyNav.prototype = {
14623     /**
14624      * @cfg {Boolean} disabled
14625      * True to disable this KeyNav instance (defaults to false)
14626      */
14627     disabled : false,
14628     /**
14629      * @cfg {String} defaultEventAction
14630      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14631      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14632      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14633      */
14634     defaultEventAction: "stopEvent",
14635     /**
14636      * @cfg {Boolean} forceKeyDown
14637      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14638      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14639      * handle keydown instead of keypress.
14640      */
14641     forceKeyDown : false,
14642
14643     // private
14644     prepareEvent : function(e){
14645         var k = e.getKey();
14646         var h = this.keyToHandler[k];
14647         //if(h && this[h]){
14648         //    e.stopPropagation();
14649         //}
14650         if(Roo.isSafari && h && k >= 37 && k <= 40){
14651             e.stopEvent();
14652         }
14653     },
14654
14655     // private
14656     relay : function(e){
14657         var k = e.getKey();
14658         var h = this.keyToHandler[k];
14659         if(h && this[h]){
14660             if(this.doRelay(e, this[h], h) !== true){
14661                 e[this.defaultEventAction]();
14662             }
14663         }
14664     },
14665
14666     // private
14667     doRelay : function(e, h, hname){
14668         return h.call(this.scope || this, e);
14669     },
14670
14671     // possible handlers
14672     enter : false,
14673     left : false,
14674     right : false,
14675     up : false,
14676     down : false,
14677     tab : false,
14678     esc : false,
14679     pageUp : false,
14680     pageDown : false,
14681     del : false,
14682     home : false,
14683     end : false,
14684
14685     // quick lookup hash
14686     keyToHandler : {
14687         37 : "left",
14688         39 : "right",
14689         38 : "up",
14690         40 : "down",
14691         33 : "pageUp",
14692         34 : "pageDown",
14693         46 : "del",
14694         36 : "home",
14695         35 : "end",
14696         13 : "enter",
14697         27 : "esc",
14698         9  : "tab"
14699     },
14700
14701         /**
14702          * Enable this KeyNav
14703          */
14704         enable: function(){
14705                 if(this.disabled){
14706             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14707             // the EventObject will normalize Safari automatically
14708             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14709                 this.el.on("keydown", this.relay,  this);
14710             }else{
14711                 this.el.on("keydown", this.prepareEvent,  this);
14712                 this.el.on("keypress", this.relay,  this);
14713             }
14714                     this.disabled = false;
14715                 }
14716         },
14717
14718         /**
14719          * Disable this KeyNav
14720          */
14721         disable: function(){
14722                 if(!this.disabled){
14723                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14724                 this.el.un("keydown", this.relay);
14725             }else{
14726                 this.el.un("keydown", this.prepareEvent);
14727                 this.el.un("keypress", this.relay);
14728             }
14729                     this.disabled = true;
14730                 }
14731         }
14732 };/*
14733  * Based on:
14734  * Ext JS Library 1.1.1
14735  * Copyright(c) 2006-2007, Ext JS, LLC.
14736  *
14737  * Originally Released Under LGPL - original licence link has changed is not relivant.
14738  *
14739  * Fork - LGPL
14740  * <script type="text/javascript">
14741  */
14742
14743  
14744 /**
14745  * @class Roo.KeyMap
14746  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14747  * The constructor accepts the same config object as defined by {@link #addBinding}.
14748  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14749  * combination it will call the function with this signature (if the match is a multi-key
14750  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14751  * A KeyMap can also handle a string representation of keys.<br />
14752  * Usage:
14753  <pre><code>
14754 // map one key by key code
14755 var map = new Roo.KeyMap("my-element", {
14756     key: 13, // or Roo.EventObject.ENTER
14757     fn: myHandler,
14758     scope: myObject
14759 });
14760
14761 // map multiple keys to one action by string
14762 var map = new Roo.KeyMap("my-element", {
14763     key: "a\r\n\t",
14764     fn: myHandler,
14765     scope: myObject
14766 });
14767
14768 // map multiple keys to multiple actions by strings and array of codes
14769 var map = new Roo.KeyMap("my-element", [
14770     {
14771         key: [10,13],
14772         fn: function(){ alert("Return was pressed"); }
14773     }, {
14774         key: "abc",
14775         fn: function(){ alert('a, b or c was pressed'); }
14776     }, {
14777         key: "\t",
14778         ctrl:true,
14779         shift:true,
14780         fn: function(){ alert('Control + shift + tab was pressed.'); }
14781     }
14782 ]);
14783 </code></pre>
14784  * <b>Note: A KeyMap starts enabled</b>
14785  * @constructor
14786  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14787  * @param {Object} config The config (see {@link #addBinding})
14788  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14789  */
14790 Roo.KeyMap = function(el, config, eventName){
14791     this.el  = Roo.get(el);
14792     this.eventName = eventName || "keydown";
14793     this.bindings = [];
14794     if(config){
14795         this.addBinding(config);
14796     }
14797     this.enable();
14798 };
14799
14800 Roo.KeyMap.prototype = {
14801     /**
14802      * True to stop the event from bubbling and prevent the default browser action if the
14803      * key was handled by the KeyMap (defaults to false)
14804      * @type Boolean
14805      */
14806     stopEvent : false,
14807
14808     /**
14809      * Add a new binding to this KeyMap. The following config object properties are supported:
14810      * <pre>
14811 Property    Type             Description
14812 ----------  ---------------  ----------------------------------------------------------------------
14813 key         String/Array     A single keycode or an array of keycodes to handle
14814 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14815 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14816 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14817 fn          Function         The function to call when KeyMap finds the expected key combination
14818 scope       Object           The scope of the callback function
14819 </pre>
14820      *
14821      * Usage:
14822      * <pre><code>
14823 // Create a KeyMap
14824 var map = new Roo.KeyMap(document, {
14825     key: Roo.EventObject.ENTER,
14826     fn: handleKey,
14827     scope: this
14828 });
14829
14830 //Add a new binding to the existing KeyMap later
14831 map.addBinding({
14832     key: 'abc',
14833     shift: true,
14834     fn: handleKey,
14835     scope: this
14836 });
14837 </code></pre>
14838      * @param {Object/Array} config A single KeyMap config or an array of configs
14839      */
14840         addBinding : function(config){
14841         if(config instanceof Array){
14842             for(var i = 0, len = config.length; i < len; i++){
14843                 this.addBinding(config[i]);
14844             }
14845             return;
14846         }
14847         var keyCode = config.key,
14848             shift = config.shift, 
14849             ctrl = config.ctrl, 
14850             alt = config.alt,
14851             fn = config.fn,
14852             scope = config.scope;
14853         if(typeof keyCode == "string"){
14854             var ks = [];
14855             var keyString = keyCode.toUpperCase();
14856             for(var j = 0, len = keyString.length; j < len; j++){
14857                 ks.push(keyString.charCodeAt(j));
14858             }
14859             keyCode = ks;
14860         }
14861         var keyArray = keyCode instanceof Array;
14862         var handler = function(e){
14863             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14864                 var k = e.getKey();
14865                 if(keyArray){
14866                     for(var i = 0, len = keyCode.length; i < len; i++){
14867                         if(keyCode[i] == k){
14868                           if(this.stopEvent){
14869                               e.stopEvent();
14870                           }
14871                           fn.call(scope || window, k, e);
14872                           return;
14873                         }
14874                     }
14875                 }else{
14876                     if(k == keyCode){
14877                         if(this.stopEvent){
14878                            e.stopEvent();
14879                         }
14880                         fn.call(scope || window, k, e);
14881                     }
14882                 }
14883             }
14884         };
14885         this.bindings.push(handler);  
14886         },
14887
14888     /**
14889      * Shorthand for adding a single key listener
14890      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14891      * following options:
14892      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14893      * @param {Function} fn The function to call
14894      * @param {Object} scope (optional) The scope of the function
14895      */
14896     on : function(key, fn, scope){
14897         var keyCode, shift, ctrl, alt;
14898         if(typeof key == "object" && !(key instanceof Array)){
14899             keyCode = key.key;
14900             shift = key.shift;
14901             ctrl = key.ctrl;
14902             alt = key.alt;
14903         }else{
14904             keyCode = key;
14905         }
14906         this.addBinding({
14907             key: keyCode,
14908             shift: shift,
14909             ctrl: ctrl,
14910             alt: alt,
14911             fn: fn,
14912             scope: scope
14913         })
14914     },
14915
14916     // private
14917     handleKeyDown : function(e){
14918             if(this.enabled){ //just in case
14919             var b = this.bindings;
14920             for(var i = 0, len = b.length; i < len; i++){
14921                 b[i].call(this, e);
14922             }
14923             }
14924         },
14925         
14926         /**
14927          * Returns true if this KeyMap is enabled
14928          * @return {Boolean} 
14929          */
14930         isEnabled : function(){
14931             return this.enabled;  
14932         },
14933         
14934         /**
14935          * Enables this KeyMap
14936          */
14937         enable: function(){
14938                 if(!this.enabled){
14939                     this.el.on(this.eventName, this.handleKeyDown, this);
14940                     this.enabled = true;
14941                 }
14942         },
14943
14944         /**
14945          * Disable this KeyMap
14946          */
14947         disable: function(){
14948                 if(this.enabled){
14949                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14950                     this.enabled = false;
14951                 }
14952         }
14953 };/*
14954  * Based on:
14955  * Ext JS Library 1.1.1
14956  * Copyright(c) 2006-2007, Ext JS, LLC.
14957  *
14958  * Originally Released Under LGPL - original licence link has changed is not relivant.
14959  *
14960  * Fork - LGPL
14961  * <script type="text/javascript">
14962  */
14963
14964  
14965 /**
14966  * @class Roo.util.TextMetrics
14967  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14968  * wide, in pixels, a given block of text will be.
14969  * @singleton
14970  */
14971 Roo.util.TextMetrics = function(){
14972     var shared;
14973     return {
14974         /**
14975          * Measures the size of the specified text
14976          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14977          * that can affect the size of the rendered text
14978          * @param {String} text The text to measure
14979          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14980          * in order to accurately measure the text height
14981          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14982          */
14983         measure : function(el, text, fixedWidth){
14984             if(!shared){
14985                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14986             }
14987             shared.bind(el);
14988             shared.setFixedWidth(fixedWidth || 'auto');
14989             return shared.getSize(text);
14990         },
14991
14992         /**
14993          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14994          * the overhead of multiple calls to initialize the style properties on each measurement.
14995          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14996          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14997          * in order to accurately measure the text height
14998          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14999          */
15000         createInstance : function(el, fixedWidth){
15001             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15002         }
15003     };
15004 }();
15005
15006  
15007
15008 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
15009     var ml = new Roo.Element(document.createElement('div'));
15010     document.body.appendChild(ml.dom);
15011     ml.position('absolute');
15012     ml.setLeftTop(-1000, -1000);
15013     ml.hide();
15014
15015     if(fixedWidth){
15016         ml.setWidth(fixedWidth);
15017     }
15018      
15019     var instance = {
15020         /**
15021          * Returns the size of the specified text based on the internal element's style and width properties
15022          * @memberOf Roo.util.TextMetrics.Instance#
15023          * @param {String} text The text to measure
15024          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15025          */
15026         getSize : function(text){
15027             ml.update(text);
15028             var s = ml.getSize();
15029             ml.update('');
15030             return s;
15031         },
15032
15033         /**
15034          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15035          * that can affect the size of the rendered text
15036          * @memberOf Roo.util.TextMetrics.Instance#
15037          * @param {String/HTMLElement} el The element, dom node or id
15038          */
15039         bind : function(el){
15040             ml.setStyle(
15041                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15042             );
15043         },
15044
15045         /**
15046          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15047          * to set a fixed width in order to accurately measure the text height.
15048          * @memberOf Roo.util.TextMetrics.Instance#
15049          * @param {Number} width The width to set on the element
15050          */
15051         setFixedWidth : function(width){
15052             ml.setWidth(width);
15053         },
15054
15055         /**
15056          * Returns the measured width of the specified text
15057          * @memberOf Roo.util.TextMetrics.Instance#
15058          * @param {String} text The text to measure
15059          * @return {Number} width The width in pixels
15060          */
15061         getWidth : function(text){
15062             ml.dom.style.width = 'auto';
15063             return this.getSize(text).width;
15064         },
15065
15066         /**
15067          * Returns the measured height of the specified text.  For multiline text, be sure to call
15068          * {@link #setFixedWidth} if necessary.
15069          * @memberOf Roo.util.TextMetrics.Instance#
15070          * @param {String} text The text to measure
15071          * @return {Number} height The height in pixels
15072          */
15073         getHeight : function(text){
15074             return this.getSize(text).height;
15075         }
15076     };
15077
15078     instance.bind(bindTo);
15079
15080     return instance;
15081 };
15082
15083 // backwards compat
15084 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15085  * Based on:
15086  * Ext JS Library 1.1.1
15087  * Copyright(c) 2006-2007, Ext JS, LLC.
15088  *
15089  * Originally Released Under LGPL - original licence link has changed is not relivant.
15090  *
15091  * Fork - LGPL
15092  * <script type="text/javascript">
15093  */
15094
15095 /**
15096  * @class Roo.state.Provider
15097  * Abstract base class for state provider implementations. This class provides methods
15098  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15099  * Provider interface.
15100  */
15101 Roo.state.Provider = function(){
15102     /**
15103      * @event statechange
15104      * Fires when a state change occurs.
15105      * @param {Provider} this This state provider
15106      * @param {String} key The state key which was changed
15107      * @param {String} value The encoded value for the state
15108      */
15109     this.addEvents({
15110         "statechange": true
15111     });
15112     this.state = {};
15113     Roo.state.Provider.superclass.constructor.call(this);
15114 };
15115 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15116     /**
15117      * Returns the current value for a key
15118      * @param {String} name The key name
15119      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15120      * @return {Mixed} The state data
15121      */
15122     get : function(name, defaultValue){
15123         return typeof this.state[name] == "undefined" ?
15124             defaultValue : this.state[name];
15125     },
15126     
15127     /**
15128      * Clears a value from the state
15129      * @param {String} name The key name
15130      */
15131     clear : function(name){
15132         delete this.state[name];
15133         this.fireEvent("statechange", this, name, null);
15134     },
15135     
15136     /**
15137      * Sets the value for a key
15138      * @param {String} name The key name
15139      * @param {Mixed} value The value to set
15140      */
15141     set : function(name, value){
15142         this.state[name] = value;
15143         this.fireEvent("statechange", this, name, value);
15144     },
15145     
15146     /**
15147      * Decodes a string previously encoded with {@link #encodeValue}.
15148      * @param {String} value The value to decode
15149      * @return {Mixed} The decoded value
15150      */
15151     decodeValue : function(cookie){
15152         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15153         var matches = re.exec(unescape(cookie));
15154         if(!matches || !matches[1]) {
15155             return; // non state cookie
15156         }
15157         var type = matches[1];
15158         var v = matches[2];
15159         switch(type){
15160             case "n":
15161                 return parseFloat(v);
15162             case "d":
15163                 return new Date(Date.parse(v));
15164             case "b":
15165                 return (v == "1");
15166             case "a":
15167                 var all = [];
15168                 var values = v.split("^");
15169                 for(var i = 0, len = values.length; i < len; i++){
15170                     all.push(this.decodeValue(values[i]));
15171                 }
15172                 return all;
15173            case "o":
15174                 var all = {};
15175                 var values = v.split("^");
15176                 for(var i = 0, len = values.length; i < len; i++){
15177                     var kv = values[i].split("=");
15178                     all[kv[0]] = this.decodeValue(kv[1]);
15179                 }
15180                 return all;
15181            default:
15182                 return v;
15183         }
15184     },
15185     
15186     /**
15187      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15188      * @param {Mixed} value The value to encode
15189      * @return {String} The encoded value
15190      */
15191     encodeValue : function(v){
15192         var enc;
15193         if(typeof v == "number"){
15194             enc = "n:" + v;
15195         }else if(typeof v == "boolean"){
15196             enc = "b:" + (v ? "1" : "0");
15197         }else if(v instanceof Date){
15198             enc = "d:" + v.toGMTString();
15199         }else if(v instanceof Array){
15200             var flat = "";
15201             for(var i = 0, len = v.length; i < len; i++){
15202                 flat += this.encodeValue(v[i]);
15203                 if(i != len-1) {
15204                     flat += "^";
15205                 }
15206             }
15207             enc = "a:" + flat;
15208         }else if(typeof v == "object"){
15209             var flat = "";
15210             for(var key in v){
15211                 if(typeof v[key] != "function"){
15212                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15213                 }
15214             }
15215             enc = "o:" + flat.substring(0, flat.length-1);
15216         }else{
15217             enc = "s:" + v;
15218         }
15219         return escape(enc);        
15220     }
15221 });
15222
15223 /*
15224  * Based on:
15225  * Ext JS Library 1.1.1
15226  * Copyright(c) 2006-2007, Ext JS, LLC.
15227  *
15228  * Originally Released Under LGPL - original licence link has changed is not relivant.
15229  *
15230  * Fork - LGPL
15231  * <script type="text/javascript">
15232  */
15233 /**
15234  * @class Roo.state.Manager
15235  * This is the global state manager. By default all components that are "state aware" check this class
15236  * for state information if you don't pass them a custom state provider. In order for this class
15237  * to be useful, it must be initialized with a provider when your application initializes.
15238  <pre><code>
15239 // in your initialization function
15240 init : function(){
15241    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15242    ...
15243    // supposed you have a {@link Roo.BorderLayout}
15244    var layout = new Roo.BorderLayout(...);
15245    layout.restoreState();
15246    // or a {Roo.BasicDialog}
15247    var dialog = new Roo.BasicDialog(...);
15248    dialog.restoreState();
15249  </code></pre>
15250  * @singleton
15251  */
15252 Roo.state.Manager = function(){
15253     var provider = new Roo.state.Provider();
15254     
15255     return {
15256         /**
15257          * Configures the default state provider for your application
15258          * @param {Provider} stateProvider The state provider to set
15259          */
15260         setProvider : function(stateProvider){
15261             provider = stateProvider;
15262         },
15263         
15264         /**
15265          * Returns the current value for a key
15266          * @param {String} name The key name
15267          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15268          * @return {Mixed} The state data
15269          */
15270         get : function(key, defaultValue){
15271             return provider.get(key, defaultValue);
15272         },
15273         
15274         /**
15275          * Sets the value for a key
15276          * @param {String} name The key name
15277          * @param {Mixed} value The state data
15278          */
15279          set : function(key, value){
15280             provider.set(key, value);
15281         },
15282         
15283         /**
15284          * Clears a value from the state
15285          * @param {String} name The key name
15286          */
15287         clear : function(key){
15288             provider.clear(key);
15289         },
15290         
15291         /**
15292          * Gets the currently configured state provider
15293          * @return {Provider} The state provider
15294          */
15295         getProvider : function(){
15296             return provider;
15297         }
15298     };
15299 }();
15300 /*
15301  * Based on:
15302  * Ext JS Library 1.1.1
15303  * Copyright(c) 2006-2007, Ext JS, LLC.
15304  *
15305  * Originally Released Under LGPL - original licence link has changed is not relivant.
15306  *
15307  * Fork - LGPL
15308  * <script type="text/javascript">
15309  */
15310 /**
15311  * @class Roo.state.CookieProvider
15312  * @extends Roo.state.Provider
15313  * The default Provider implementation which saves state via cookies.
15314  * <br />Usage:
15315  <pre><code>
15316    var cp = new Roo.state.CookieProvider({
15317        path: "/cgi-bin/",
15318        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15319        domain: "roojs.com"
15320    })
15321    Roo.state.Manager.setProvider(cp);
15322  </code></pre>
15323  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15324  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15325  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15326  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15327  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15328  * domain the page is running on including the 'www' like 'www.roojs.com')
15329  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15330  * @constructor
15331  * Create a new CookieProvider
15332  * @param {Object} config The configuration object
15333  */
15334 Roo.state.CookieProvider = function(config){
15335     Roo.state.CookieProvider.superclass.constructor.call(this);
15336     this.path = "/";
15337     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15338     this.domain = null;
15339     this.secure = false;
15340     Roo.apply(this, config);
15341     this.state = this.readCookies();
15342 };
15343
15344 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15345     // private
15346     set : function(name, value){
15347         if(typeof value == "undefined" || value === null){
15348             this.clear(name);
15349             return;
15350         }
15351         this.setCookie(name, value);
15352         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15353     },
15354
15355     // private
15356     clear : function(name){
15357         this.clearCookie(name);
15358         Roo.state.CookieProvider.superclass.clear.call(this, name);
15359     },
15360
15361     // private
15362     readCookies : function(){
15363         var cookies = {};
15364         var c = document.cookie + ";";
15365         var re = /\s?(.*?)=(.*?);/g;
15366         var matches;
15367         while((matches = re.exec(c)) != null){
15368             var name = matches[1];
15369             var value = matches[2];
15370             if(name && name.substring(0,3) == "ys-"){
15371                 cookies[name.substr(3)] = this.decodeValue(value);
15372             }
15373         }
15374         return cookies;
15375     },
15376
15377     // private
15378     setCookie : function(name, value){
15379         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15380            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15381            ((this.path == null) ? "" : ("; path=" + this.path)) +
15382            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15383            ((this.secure == true) ? "; secure" : "");
15384     },
15385
15386     // private
15387     clearCookie : function(name){
15388         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15389            ((this.path == null) ? "" : ("; path=" + this.path)) +
15390            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15391            ((this.secure == true) ? "; secure" : "");
15392     }
15393 });/*
15394  * Based on:
15395  * Ext JS Library 1.1.1
15396  * Copyright(c) 2006-2007, Ext JS, LLC.
15397  *
15398  * Originally Released Under LGPL - original licence link has changed is not relivant.
15399  *
15400  * Fork - LGPL
15401  * <script type="text/javascript">
15402  */
15403  
15404
15405 /**
15406  * @class Roo.ComponentMgr
15407  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15408  * @singleton
15409  */
15410 Roo.ComponentMgr = function(){
15411     var all = new Roo.util.MixedCollection();
15412
15413     return {
15414         /**
15415          * Registers a component.
15416          * @param {Roo.Component} c The component
15417          */
15418         register : function(c){
15419             all.add(c);
15420         },
15421
15422         /**
15423          * Unregisters a component.
15424          * @param {Roo.Component} c The component
15425          */
15426         unregister : function(c){
15427             all.remove(c);
15428         },
15429
15430         /**
15431          * Returns a component by id
15432          * @param {String} id The component id
15433          */
15434         get : function(id){
15435             return all.get(id);
15436         },
15437
15438         /**
15439          * Registers a function that will be called when a specified component is added to ComponentMgr
15440          * @param {String} id The component id
15441          * @param {Funtction} fn The callback function
15442          * @param {Object} scope The scope of the callback
15443          */
15444         onAvailable : function(id, fn, scope){
15445             all.on("add", function(index, o){
15446                 if(o.id == id){
15447                     fn.call(scope || o, o);
15448                     all.un("add", fn, scope);
15449                 }
15450             });
15451         }
15452     };
15453 }();/*
15454  * Based on:
15455  * Ext JS Library 1.1.1
15456  * Copyright(c) 2006-2007, Ext JS, LLC.
15457  *
15458  * Originally Released Under LGPL - original licence link has changed is not relivant.
15459  *
15460  * Fork - LGPL
15461  * <script type="text/javascript">
15462  */
15463  
15464 /**
15465  * @class Roo.Component
15466  * @extends Roo.util.Observable
15467  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15468  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15469  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15470  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15471  * All visual components (widgets) that require rendering into a layout should subclass Component.
15472  * @constructor
15473  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15474  * 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
15475  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15476  */
15477 Roo.Component = function(config){
15478     config = config || {};
15479     if(config.tagName || config.dom || typeof config == "string"){ // element object
15480         config = {el: config, id: config.id || config};
15481     }
15482     this.initialConfig = config;
15483
15484     Roo.apply(this, config);
15485     this.addEvents({
15486         /**
15487          * @event disable
15488          * Fires after the component is disabled.
15489              * @param {Roo.Component} this
15490              */
15491         disable : true,
15492         /**
15493          * @event enable
15494          * Fires after the component is enabled.
15495              * @param {Roo.Component} this
15496              */
15497         enable : true,
15498         /**
15499          * @event beforeshow
15500          * Fires before the component is shown.  Return false to stop the show.
15501              * @param {Roo.Component} this
15502              */
15503         beforeshow : true,
15504         /**
15505          * @event show
15506          * Fires after the component is shown.
15507              * @param {Roo.Component} this
15508              */
15509         show : true,
15510         /**
15511          * @event beforehide
15512          * Fires before the component is hidden. Return false to stop the hide.
15513              * @param {Roo.Component} this
15514              */
15515         beforehide : true,
15516         /**
15517          * @event hide
15518          * Fires after the component is hidden.
15519              * @param {Roo.Component} this
15520              */
15521         hide : true,
15522         /**
15523          * @event beforerender
15524          * Fires before the component is rendered. Return false to stop the render.
15525              * @param {Roo.Component} this
15526              */
15527         beforerender : true,
15528         /**
15529          * @event render
15530          * Fires after the component is rendered.
15531              * @param {Roo.Component} this
15532              */
15533         render : true,
15534         /**
15535          * @event beforedestroy
15536          * Fires before the component is destroyed. Return false to stop the destroy.
15537              * @param {Roo.Component} this
15538              */
15539         beforedestroy : true,
15540         /**
15541          * @event destroy
15542          * Fires after the component is destroyed.
15543              * @param {Roo.Component} this
15544              */
15545         destroy : true
15546     });
15547     if(!this.id){
15548         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15549     }
15550     Roo.ComponentMgr.register(this);
15551     Roo.Component.superclass.constructor.call(this);
15552     this.initComponent();
15553     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15554         this.render(this.renderTo);
15555         delete this.renderTo;
15556     }
15557 };
15558
15559 /** @private */
15560 Roo.Component.AUTO_ID = 1000;
15561
15562 Roo.extend(Roo.Component, Roo.util.Observable, {
15563     /**
15564      * @scope Roo.Component.prototype
15565      * @type {Boolean}
15566      * true if this component is hidden. Read-only.
15567      */
15568     hidden : false,
15569     /**
15570      * @type {Boolean}
15571      * true if this component is disabled. Read-only.
15572      */
15573     disabled : false,
15574     /**
15575      * @type {Boolean}
15576      * true if this component has been rendered. Read-only.
15577      */
15578     rendered : false,
15579     
15580     /** @cfg {String} disableClass
15581      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15582      */
15583     disabledClass : "x-item-disabled",
15584         /** @cfg {Boolean} allowDomMove
15585          * Whether the component can move the Dom node when rendering (defaults to true).
15586          */
15587     allowDomMove : true,
15588     /** @cfg {String} hideMode (display|visibility)
15589      * How this component should hidden. Supported values are
15590      * "visibility" (css visibility), "offsets" (negative offset position) and
15591      * "display" (css display) - defaults to "display".
15592      */
15593     hideMode: 'display',
15594
15595     /** @private */
15596     ctype : "Roo.Component",
15597
15598     /**
15599      * @cfg {String} actionMode 
15600      * which property holds the element that used for  hide() / show() / disable() / enable()
15601      * default is 'el' for forms you probably want to set this to fieldEl 
15602      */
15603     actionMode : "el",
15604
15605     /** @private */
15606     getActionEl : function(){
15607         return this[this.actionMode];
15608     },
15609
15610     initComponent : Roo.emptyFn,
15611     /**
15612      * If this is a lazy rendering component, render it to its container element.
15613      * @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.
15614      */
15615     render : function(container, position){
15616         
15617         if(this.rendered){
15618             return this;
15619         }
15620         
15621         if(this.fireEvent("beforerender", this) === false){
15622             return false;
15623         }
15624         
15625         if(!container && this.el){
15626             this.el = Roo.get(this.el);
15627             container = this.el.dom.parentNode;
15628             this.allowDomMove = false;
15629         }
15630         this.container = Roo.get(container);
15631         this.rendered = true;
15632         if(position !== undefined){
15633             if(typeof position == 'number'){
15634                 position = this.container.dom.childNodes[position];
15635             }else{
15636                 position = Roo.getDom(position);
15637             }
15638         }
15639         this.onRender(this.container, position || null);
15640         if(this.cls){
15641             this.el.addClass(this.cls);
15642             delete this.cls;
15643         }
15644         if(this.style){
15645             this.el.applyStyles(this.style);
15646             delete this.style;
15647         }
15648         this.fireEvent("render", this);
15649         this.afterRender(this.container);
15650         if(this.hidden){
15651             this.hide();
15652         }
15653         if(this.disabled){
15654             this.disable();
15655         }
15656
15657         return this;
15658         
15659     },
15660
15661     /** @private */
15662     // default function is not really useful
15663     onRender : function(ct, position){
15664         if(this.el){
15665             this.el = Roo.get(this.el);
15666             if(this.allowDomMove !== false){
15667                 ct.dom.insertBefore(this.el.dom, position);
15668             }
15669         }
15670     },
15671
15672     /** @private */
15673     getAutoCreate : function(){
15674         var cfg = typeof this.autoCreate == "object" ?
15675                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15676         if(this.id && !cfg.id){
15677             cfg.id = this.id;
15678         }
15679         return cfg;
15680     },
15681
15682     /** @private */
15683     afterRender : Roo.emptyFn,
15684
15685     /**
15686      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15687      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15688      */
15689     destroy : function(){
15690         if(this.fireEvent("beforedestroy", this) !== false){
15691             this.purgeListeners();
15692             this.beforeDestroy();
15693             if(this.rendered){
15694                 this.el.removeAllListeners();
15695                 this.el.remove();
15696                 if(this.actionMode == "container"){
15697                     this.container.remove();
15698                 }
15699             }
15700             this.onDestroy();
15701             Roo.ComponentMgr.unregister(this);
15702             this.fireEvent("destroy", this);
15703         }
15704     },
15705
15706         /** @private */
15707     beforeDestroy : function(){
15708
15709     },
15710
15711         /** @private */
15712         onDestroy : function(){
15713
15714     },
15715
15716     /**
15717      * Returns the underlying {@link Roo.Element}.
15718      * @return {Roo.Element} The element
15719      */
15720     getEl : function(){
15721         return this.el;
15722     },
15723
15724     /**
15725      * Returns the id of this component.
15726      * @return {String}
15727      */
15728     getId : function(){
15729         return this.id;
15730     },
15731
15732     /**
15733      * Try to focus this component.
15734      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15735      * @return {Roo.Component} this
15736      */
15737     focus : function(selectText){
15738         if(this.rendered){
15739             this.el.focus();
15740             if(selectText === true){
15741                 this.el.dom.select();
15742             }
15743         }
15744         return this;
15745     },
15746
15747     /** @private */
15748     blur : function(){
15749         if(this.rendered){
15750             this.el.blur();
15751         }
15752         return this;
15753     },
15754
15755     /**
15756      * Disable this component.
15757      * @return {Roo.Component} this
15758      */
15759     disable : function(){
15760         if(this.rendered){
15761             this.onDisable();
15762         }
15763         this.disabled = true;
15764         this.fireEvent("disable", this);
15765         return this;
15766     },
15767
15768         // private
15769     onDisable : function(){
15770         this.getActionEl().addClass(this.disabledClass);
15771         this.el.dom.disabled = true;
15772     },
15773
15774     /**
15775      * Enable this component.
15776      * @return {Roo.Component} this
15777      */
15778     enable : function(){
15779         if(this.rendered){
15780             this.onEnable();
15781         }
15782         this.disabled = false;
15783         this.fireEvent("enable", this);
15784         return this;
15785     },
15786
15787         // private
15788     onEnable : function(){
15789         this.getActionEl().removeClass(this.disabledClass);
15790         this.el.dom.disabled = false;
15791     },
15792
15793     /**
15794      * Convenience function for setting disabled/enabled by boolean.
15795      * @param {Boolean} disabled
15796      */
15797     setDisabled : function(disabled){
15798         this[disabled ? "disable" : "enable"]();
15799     },
15800
15801     /**
15802      * Show this component.
15803      * @return {Roo.Component} this
15804      */
15805     show: function(){
15806         if(this.fireEvent("beforeshow", this) !== false){
15807             this.hidden = false;
15808             if(this.rendered){
15809                 this.onShow();
15810             }
15811             this.fireEvent("show", this);
15812         }
15813         return this;
15814     },
15815
15816     // private
15817     onShow : function(){
15818         var ae = this.getActionEl();
15819         if(this.hideMode == 'visibility'){
15820             ae.dom.style.visibility = "visible";
15821         }else if(this.hideMode == 'offsets'){
15822             ae.removeClass('x-hidden');
15823         }else{
15824             ae.dom.style.display = "";
15825         }
15826     },
15827
15828     /**
15829      * Hide this component.
15830      * @return {Roo.Component} this
15831      */
15832     hide: function(){
15833         if(this.fireEvent("beforehide", this) !== false){
15834             this.hidden = true;
15835             if(this.rendered){
15836                 this.onHide();
15837             }
15838             this.fireEvent("hide", this);
15839         }
15840         return this;
15841     },
15842
15843     // private
15844     onHide : function(){
15845         var ae = this.getActionEl();
15846         if(this.hideMode == 'visibility'){
15847             ae.dom.style.visibility = "hidden";
15848         }else if(this.hideMode == 'offsets'){
15849             ae.addClass('x-hidden');
15850         }else{
15851             ae.dom.style.display = "none";
15852         }
15853     },
15854
15855     /**
15856      * Convenience function to hide or show this component by boolean.
15857      * @param {Boolean} visible True to show, false to hide
15858      * @return {Roo.Component} this
15859      */
15860     setVisible: function(visible){
15861         if(visible) {
15862             this.show();
15863         }else{
15864             this.hide();
15865         }
15866         return this;
15867     },
15868
15869     /**
15870      * Returns true if this component is visible.
15871      */
15872     isVisible : function(){
15873         return this.getActionEl().isVisible();
15874     },
15875
15876     cloneConfig : function(overrides){
15877         overrides = overrides || {};
15878         var id = overrides.id || Roo.id();
15879         var cfg = Roo.applyIf(overrides, this.initialConfig);
15880         cfg.id = id; // prevent dup id
15881         return new this.constructor(cfg);
15882     }
15883 });/*
15884  * Based on:
15885  * Ext JS Library 1.1.1
15886  * Copyright(c) 2006-2007, Ext JS, LLC.
15887  *
15888  * Originally Released Under LGPL - original licence link has changed is not relivant.
15889  *
15890  * Fork - LGPL
15891  * <script type="text/javascript">
15892  */
15893
15894 /**
15895  * @class Roo.BoxComponent
15896  * @extends Roo.Component
15897  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15898  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15899  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15900  * layout containers.
15901  * @constructor
15902  * @param {Roo.Element/String/Object} config The configuration options.
15903  */
15904 Roo.BoxComponent = function(config){
15905     Roo.Component.call(this, config);
15906     this.addEvents({
15907         /**
15908          * @event resize
15909          * Fires after the component is resized.
15910              * @param {Roo.Component} this
15911              * @param {Number} adjWidth The box-adjusted width that was set
15912              * @param {Number} adjHeight The box-adjusted height that was set
15913              * @param {Number} rawWidth The width that was originally specified
15914              * @param {Number} rawHeight The height that was originally specified
15915              */
15916         resize : true,
15917         /**
15918          * @event move
15919          * Fires after the component is moved.
15920              * @param {Roo.Component} this
15921              * @param {Number} x The new x position
15922              * @param {Number} y The new y position
15923              */
15924         move : true
15925     });
15926 };
15927
15928 Roo.extend(Roo.BoxComponent, Roo.Component, {
15929     // private, set in afterRender to signify that the component has been rendered
15930     boxReady : false,
15931     // private, used to defer height settings to subclasses
15932     deferHeight: false,
15933     /** @cfg {Number} width
15934      * width (optional) size of component
15935      */
15936      /** @cfg {Number} height
15937      * height (optional) size of component
15938      */
15939      
15940     /**
15941      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15942      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15943      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15944      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15945      * @return {Roo.BoxComponent} this
15946      */
15947     setSize : function(w, h){
15948         // support for standard size objects
15949         if(typeof w == 'object'){
15950             h = w.height;
15951             w = w.width;
15952         }
15953         // not rendered
15954         if(!this.boxReady){
15955             this.width = w;
15956             this.height = h;
15957             return this;
15958         }
15959
15960         // prevent recalcs when not needed
15961         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15962             return this;
15963         }
15964         this.lastSize = {width: w, height: h};
15965
15966         var adj = this.adjustSize(w, h);
15967         var aw = adj.width, ah = adj.height;
15968         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15969             var rz = this.getResizeEl();
15970             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15971                 rz.setSize(aw, ah);
15972             }else if(!this.deferHeight && ah !== undefined){
15973                 rz.setHeight(ah);
15974             }else if(aw !== undefined){
15975                 rz.setWidth(aw);
15976             }
15977             this.onResize(aw, ah, w, h);
15978             this.fireEvent('resize', this, aw, ah, w, h);
15979         }
15980         return this;
15981     },
15982
15983     /**
15984      * Gets the current size of the component's underlying element.
15985      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15986      */
15987     getSize : function(){
15988         return this.el.getSize();
15989     },
15990
15991     /**
15992      * Gets the current XY position of the component's underlying element.
15993      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15994      * @return {Array} The XY position of the element (e.g., [100, 200])
15995      */
15996     getPosition : function(local){
15997         if(local === true){
15998             return [this.el.getLeft(true), this.el.getTop(true)];
15999         }
16000         return this.xy || this.el.getXY();
16001     },
16002
16003     /**
16004      * Gets the current box measurements of the component's underlying element.
16005      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16006      * @returns {Object} box An object in the format {x, y, width, height}
16007      */
16008     getBox : function(local){
16009         var s = this.el.getSize();
16010         if(local){
16011             s.x = this.el.getLeft(true);
16012             s.y = this.el.getTop(true);
16013         }else{
16014             var xy = this.xy || this.el.getXY();
16015             s.x = xy[0];
16016             s.y = xy[1];
16017         }
16018         return s;
16019     },
16020
16021     /**
16022      * Sets the current box measurements of the component's underlying element.
16023      * @param {Object} box An object in the format {x, y, width, height}
16024      * @returns {Roo.BoxComponent} this
16025      */
16026     updateBox : function(box){
16027         this.setSize(box.width, box.height);
16028         this.setPagePosition(box.x, box.y);
16029         return this;
16030     },
16031
16032     // protected
16033     getResizeEl : function(){
16034         return this.resizeEl || this.el;
16035     },
16036
16037     // protected
16038     getPositionEl : function(){
16039         return this.positionEl || this.el;
16040     },
16041
16042     /**
16043      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16044      * This method fires the move event.
16045      * @param {Number} left The new left
16046      * @param {Number} top The new top
16047      * @returns {Roo.BoxComponent} this
16048      */
16049     setPosition : function(x, y){
16050         this.x = x;
16051         this.y = y;
16052         if(!this.boxReady){
16053             return this;
16054         }
16055         var adj = this.adjustPosition(x, y);
16056         var ax = adj.x, ay = adj.y;
16057
16058         var el = this.getPositionEl();
16059         if(ax !== undefined || ay !== undefined){
16060             if(ax !== undefined && ay !== undefined){
16061                 el.setLeftTop(ax, ay);
16062             }else if(ax !== undefined){
16063                 el.setLeft(ax);
16064             }else if(ay !== undefined){
16065                 el.setTop(ay);
16066             }
16067             this.onPosition(ax, ay);
16068             this.fireEvent('move', this, ax, ay);
16069         }
16070         return this;
16071     },
16072
16073     /**
16074      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16075      * This method fires the move event.
16076      * @param {Number} x The new x position
16077      * @param {Number} y The new y position
16078      * @returns {Roo.BoxComponent} this
16079      */
16080     setPagePosition : function(x, y){
16081         this.pageX = x;
16082         this.pageY = y;
16083         if(!this.boxReady){
16084             return;
16085         }
16086         if(x === undefined || y === undefined){ // cannot translate undefined points
16087             return;
16088         }
16089         var p = this.el.translatePoints(x, y);
16090         this.setPosition(p.left, p.top);
16091         return this;
16092     },
16093
16094     // private
16095     onRender : function(ct, position){
16096         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16097         if(this.resizeEl){
16098             this.resizeEl = Roo.get(this.resizeEl);
16099         }
16100         if(this.positionEl){
16101             this.positionEl = Roo.get(this.positionEl);
16102         }
16103     },
16104
16105     // private
16106     afterRender : function(){
16107         Roo.BoxComponent.superclass.afterRender.call(this);
16108         this.boxReady = true;
16109         this.setSize(this.width, this.height);
16110         if(this.x || this.y){
16111             this.setPosition(this.x, this.y);
16112         }
16113         if(this.pageX || this.pageY){
16114             this.setPagePosition(this.pageX, this.pageY);
16115         }
16116     },
16117
16118     /**
16119      * Force the component's size to recalculate based on the underlying element's current height and width.
16120      * @returns {Roo.BoxComponent} this
16121      */
16122     syncSize : function(){
16123         delete this.lastSize;
16124         this.setSize(this.el.getWidth(), this.el.getHeight());
16125         return this;
16126     },
16127
16128     /**
16129      * Called after the component is resized, this method is empty by default but can be implemented by any
16130      * subclass that needs to perform custom logic after a resize occurs.
16131      * @param {Number} adjWidth The box-adjusted width that was set
16132      * @param {Number} adjHeight The box-adjusted height that was set
16133      * @param {Number} rawWidth The width that was originally specified
16134      * @param {Number} rawHeight The height that was originally specified
16135      */
16136     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16137
16138     },
16139
16140     /**
16141      * Called after the component is moved, this method is empty by default but can be implemented by any
16142      * subclass that needs to perform custom logic after a move occurs.
16143      * @param {Number} x The new x position
16144      * @param {Number} y The new y position
16145      */
16146     onPosition : function(x, y){
16147
16148     },
16149
16150     // private
16151     adjustSize : function(w, h){
16152         if(this.autoWidth){
16153             w = 'auto';
16154         }
16155         if(this.autoHeight){
16156             h = 'auto';
16157         }
16158         return {width : w, height: h};
16159     },
16160
16161     // private
16162     adjustPosition : function(x, y){
16163         return {x : x, y: y};
16164     }
16165 });/*
16166  * Based on:
16167  * Ext JS Library 1.1.1
16168  * Copyright(c) 2006-2007, Ext JS, LLC.
16169  *
16170  * Originally Released Under LGPL - original licence link has changed is not relivant.
16171  *
16172  * Fork - LGPL
16173  * <script type="text/javascript">
16174  */
16175  (function(){ 
16176 /**
16177  * @class Roo.Layer
16178  * @extends Roo.Element
16179  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16180  * automatic maintaining of shadow/shim positions.
16181  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16182  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16183  * you can pass a string with a CSS class name. False turns off the shadow.
16184  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16185  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16186  * @cfg {String} cls CSS class to add to the element
16187  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16188  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16189  * @constructor
16190  * @param {Object} config An object with config options.
16191  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16192  */
16193
16194 Roo.Layer = function(config, existingEl){
16195     config = config || {};
16196     var dh = Roo.DomHelper;
16197     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16198     if(existingEl){
16199         this.dom = Roo.getDom(existingEl);
16200     }
16201     if(!this.dom){
16202         var o = config.dh || {tag: "div", cls: "x-layer"};
16203         this.dom = dh.append(pel, o);
16204     }
16205     if(config.cls){
16206         this.addClass(config.cls);
16207     }
16208     this.constrain = config.constrain !== false;
16209     this.visibilityMode = Roo.Element.VISIBILITY;
16210     if(config.id){
16211         this.id = this.dom.id = config.id;
16212     }else{
16213         this.id = Roo.id(this.dom);
16214     }
16215     this.zindex = config.zindex || this.getZIndex();
16216     this.position("absolute", this.zindex);
16217     if(config.shadow){
16218         this.shadowOffset = config.shadowOffset || 4;
16219         this.shadow = new Roo.Shadow({
16220             offset : this.shadowOffset,
16221             mode : config.shadow
16222         });
16223     }else{
16224         this.shadowOffset = 0;
16225     }
16226     this.useShim = config.shim !== false && Roo.useShims;
16227     this.useDisplay = config.useDisplay;
16228     this.hide();
16229 };
16230
16231 var supr = Roo.Element.prototype;
16232
16233 // shims are shared among layer to keep from having 100 iframes
16234 var shims = [];
16235
16236 Roo.extend(Roo.Layer, Roo.Element, {
16237
16238     getZIndex : function(){
16239         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16240     },
16241
16242     getShim : function(){
16243         if(!this.useShim){
16244             return null;
16245         }
16246         if(this.shim){
16247             return this.shim;
16248         }
16249         var shim = shims.shift();
16250         if(!shim){
16251             shim = this.createShim();
16252             shim.enableDisplayMode('block');
16253             shim.dom.style.display = 'none';
16254             shim.dom.style.visibility = 'visible';
16255         }
16256         var pn = this.dom.parentNode;
16257         if(shim.dom.parentNode != pn){
16258             pn.insertBefore(shim.dom, this.dom);
16259         }
16260         shim.setStyle('z-index', this.getZIndex()-2);
16261         this.shim = shim;
16262         return shim;
16263     },
16264
16265     hideShim : function(){
16266         if(this.shim){
16267             this.shim.setDisplayed(false);
16268             shims.push(this.shim);
16269             delete this.shim;
16270         }
16271     },
16272
16273     disableShadow : function(){
16274         if(this.shadow){
16275             this.shadowDisabled = true;
16276             this.shadow.hide();
16277             this.lastShadowOffset = this.shadowOffset;
16278             this.shadowOffset = 0;
16279         }
16280     },
16281
16282     enableShadow : function(show){
16283         if(this.shadow){
16284             this.shadowDisabled = false;
16285             this.shadowOffset = this.lastShadowOffset;
16286             delete this.lastShadowOffset;
16287             if(show){
16288                 this.sync(true);
16289             }
16290         }
16291     },
16292
16293     // private
16294     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16295     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16296     sync : function(doShow){
16297         var sw = this.shadow;
16298         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16299             var sh = this.getShim();
16300
16301             var w = this.getWidth(),
16302                 h = this.getHeight();
16303
16304             var l = this.getLeft(true),
16305                 t = this.getTop(true);
16306
16307             if(sw && !this.shadowDisabled){
16308                 if(doShow && !sw.isVisible()){
16309                     sw.show(this);
16310                 }else{
16311                     sw.realign(l, t, w, h);
16312                 }
16313                 if(sh){
16314                     if(doShow){
16315                        sh.show();
16316                     }
16317                     // fit the shim behind the shadow, so it is shimmed too
16318                     var a = sw.adjusts, s = sh.dom.style;
16319                     s.left = (Math.min(l, l+a.l))+"px";
16320                     s.top = (Math.min(t, t+a.t))+"px";
16321                     s.width = (w+a.w)+"px";
16322                     s.height = (h+a.h)+"px";
16323                 }
16324             }else if(sh){
16325                 if(doShow){
16326                    sh.show();
16327                 }
16328                 sh.setSize(w, h);
16329                 sh.setLeftTop(l, t);
16330             }
16331             
16332         }
16333     },
16334
16335     // private
16336     destroy : function(){
16337         this.hideShim();
16338         if(this.shadow){
16339             this.shadow.hide();
16340         }
16341         this.removeAllListeners();
16342         var pn = this.dom.parentNode;
16343         if(pn){
16344             pn.removeChild(this.dom);
16345         }
16346         Roo.Element.uncache(this.id);
16347     },
16348
16349     remove : function(){
16350         this.destroy();
16351     },
16352
16353     // private
16354     beginUpdate : function(){
16355         this.updating = true;
16356     },
16357
16358     // private
16359     endUpdate : function(){
16360         this.updating = false;
16361         this.sync(true);
16362     },
16363
16364     // private
16365     hideUnders : function(negOffset){
16366         if(this.shadow){
16367             this.shadow.hide();
16368         }
16369         this.hideShim();
16370     },
16371
16372     // private
16373     constrainXY : function(){
16374         if(this.constrain){
16375             var vw = Roo.lib.Dom.getViewWidth(),
16376                 vh = Roo.lib.Dom.getViewHeight();
16377             var s = Roo.get(document).getScroll();
16378
16379             var xy = this.getXY();
16380             var x = xy[0], y = xy[1];   
16381             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16382             // only move it if it needs it
16383             var moved = false;
16384             // first validate right/bottom
16385             if((x + w) > vw+s.left){
16386                 x = vw - w - this.shadowOffset;
16387                 moved = true;
16388             }
16389             if((y + h) > vh+s.top){
16390                 y = vh - h - this.shadowOffset;
16391                 moved = true;
16392             }
16393             // then make sure top/left isn't negative
16394             if(x < s.left){
16395                 x = s.left;
16396                 moved = true;
16397             }
16398             if(y < s.top){
16399                 y = s.top;
16400                 moved = true;
16401             }
16402             if(moved){
16403                 if(this.avoidY){
16404                     var ay = this.avoidY;
16405                     if(y <= ay && (y+h) >= ay){
16406                         y = ay-h-5;   
16407                     }
16408                 }
16409                 xy = [x, y];
16410                 this.storeXY(xy);
16411                 supr.setXY.call(this, xy);
16412                 this.sync();
16413             }
16414         }
16415     },
16416
16417     isVisible : function(){
16418         return this.visible;    
16419     },
16420
16421     // private
16422     showAction : function(){
16423         this.visible = true; // track visibility to prevent getStyle calls
16424         if(this.useDisplay === true){
16425             this.setDisplayed("");
16426         }else if(this.lastXY){
16427             supr.setXY.call(this, this.lastXY);
16428         }else if(this.lastLT){
16429             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16430         }
16431     },
16432
16433     // private
16434     hideAction : function(){
16435         this.visible = false;
16436         if(this.useDisplay === true){
16437             this.setDisplayed(false);
16438         }else{
16439             this.setLeftTop(-10000,-10000);
16440         }
16441     },
16442
16443     // overridden Element method
16444     setVisible : function(v, a, d, c, e){
16445         if(v){
16446             this.showAction();
16447         }
16448         if(a && v){
16449             var cb = function(){
16450                 this.sync(true);
16451                 if(c){
16452                     c();
16453                 }
16454             }.createDelegate(this);
16455             supr.setVisible.call(this, true, true, d, cb, e);
16456         }else{
16457             if(!v){
16458                 this.hideUnders(true);
16459             }
16460             var cb = c;
16461             if(a){
16462                 cb = function(){
16463                     this.hideAction();
16464                     if(c){
16465                         c();
16466                     }
16467                 }.createDelegate(this);
16468             }
16469             supr.setVisible.call(this, v, a, d, cb, e);
16470             if(v){
16471                 this.sync(true);
16472             }else if(!a){
16473                 this.hideAction();
16474             }
16475         }
16476     },
16477
16478     storeXY : function(xy){
16479         delete this.lastLT;
16480         this.lastXY = xy;
16481     },
16482
16483     storeLeftTop : function(left, top){
16484         delete this.lastXY;
16485         this.lastLT = [left, top];
16486     },
16487
16488     // private
16489     beforeFx : function(){
16490         this.beforeAction();
16491         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16492     },
16493
16494     // private
16495     afterFx : function(){
16496         Roo.Layer.superclass.afterFx.apply(this, arguments);
16497         this.sync(this.isVisible());
16498     },
16499
16500     // private
16501     beforeAction : function(){
16502         if(!this.updating && this.shadow){
16503             this.shadow.hide();
16504         }
16505     },
16506
16507     // overridden Element method
16508     setLeft : function(left){
16509         this.storeLeftTop(left, this.getTop(true));
16510         supr.setLeft.apply(this, arguments);
16511         this.sync();
16512     },
16513
16514     setTop : function(top){
16515         this.storeLeftTop(this.getLeft(true), top);
16516         supr.setTop.apply(this, arguments);
16517         this.sync();
16518     },
16519
16520     setLeftTop : function(left, top){
16521         this.storeLeftTop(left, top);
16522         supr.setLeftTop.apply(this, arguments);
16523         this.sync();
16524     },
16525
16526     setXY : function(xy, a, d, c, e){
16527         this.fixDisplay();
16528         this.beforeAction();
16529         this.storeXY(xy);
16530         var cb = this.createCB(c);
16531         supr.setXY.call(this, xy, a, d, cb, e);
16532         if(!a){
16533             cb();
16534         }
16535     },
16536
16537     // private
16538     createCB : function(c){
16539         var el = this;
16540         return function(){
16541             el.constrainXY();
16542             el.sync(true);
16543             if(c){
16544                 c();
16545             }
16546         };
16547     },
16548
16549     // overridden Element method
16550     setX : function(x, a, d, c, e){
16551         this.setXY([x, this.getY()], a, d, c, e);
16552     },
16553
16554     // overridden Element method
16555     setY : function(y, a, d, c, e){
16556         this.setXY([this.getX(), y], a, d, c, e);
16557     },
16558
16559     // overridden Element method
16560     setSize : function(w, h, a, d, c, e){
16561         this.beforeAction();
16562         var cb = this.createCB(c);
16563         supr.setSize.call(this, w, h, a, d, cb, e);
16564         if(!a){
16565             cb();
16566         }
16567     },
16568
16569     // overridden Element method
16570     setWidth : function(w, a, d, c, e){
16571         this.beforeAction();
16572         var cb = this.createCB(c);
16573         supr.setWidth.call(this, w, a, d, cb, e);
16574         if(!a){
16575             cb();
16576         }
16577     },
16578
16579     // overridden Element method
16580     setHeight : function(h, a, d, c, e){
16581         this.beforeAction();
16582         var cb = this.createCB(c);
16583         supr.setHeight.call(this, h, a, d, cb, e);
16584         if(!a){
16585             cb();
16586         }
16587     },
16588
16589     // overridden Element method
16590     setBounds : function(x, y, w, h, a, d, c, e){
16591         this.beforeAction();
16592         var cb = this.createCB(c);
16593         if(!a){
16594             this.storeXY([x, y]);
16595             supr.setXY.call(this, [x, y]);
16596             supr.setSize.call(this, w, h, a, d, cb, e);
16597             cb();
16598         }else{
16599             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16600         }
16601         return this;
16602     },
16603     
16604     /**
16605      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16606      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16607      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16608      * @param {Number} zindex The new z-index to set
16609      * @return {this} The Layer
16610      */
16611     setZIndex : function(zindex){
16612         this.zindex = zindex;
16613         this.setStyle("z-index", zindex + 2);
16614         if(this.shadow){
16615             this.shadow.setZIndex(zindex + 1);
16616         }
16617         if(this.shim){
16618             this.shim.setStyle("z-index", zindex);
16619         }
16620     }
16621 });
16622 })();/*
16623  * Original code for Roojs - LGPL
16624  * <script type="text/javascript">
16625  */
16626  
16627 /**
16628  * @class Roo.XComponent
16629  * A delayed Element creator...
16630  * Or a way to group chunks of interface together.
16631  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16632  *  used in conjunction with XComponent.build() it will create an instance of each element,
16633  *  then call addxtype() to build the User interface.
16634  * 
16635  * Mypart.xyx = new Roo.XComponent({
16636
16637     parent : 'Mypart.xyz', // empty == document.element.!!
16638     order : '001',
16639     name : 'xxxx'
16640     region : 'xxxx'
16641     disabled : function() {} 
16642      
16643     tree : function() { // return an tree of xtype declared components
16644         var MODULE = this;
16645         return 
16646         {
16647             xtype : 'NestedLayoutPanel',
16648             // technicall
16649         }
16650      ]
16651  *})
16652  *
16653  *
16654  * It can be used to build a big heiracy, with parent etc.
16655  * or you can just use this to render a single compoent to a dom element
16656  * MYPART.render(Roo.Element | String(id) | dom_element )
16657  *
16658  *
16659  * Usage patterns.
16660  *
16661  * Classic Roo
16662  *
16663  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16664  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16665  *
16666  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16667  *
16668  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16669  * - if mulitple topModules exist, the last one is defined as the top module.
16670  *
16671  * Embeded Roo
16672  * 
16673  * When the top level or multiple modules are to embedded into a existing HTML page,
16674  * the parent element can container '#id' of the element where the module will be drawn.
16675  *
16676  * Bootstrap Roo
16677  *
16678  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16679  * it relies more on a include mechanism, where sub modules are included into an outer page.
16680  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16681  * 
16682  * Bootstrap Roo Included elements
16683  *
16684  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16685  * hence confusing the component builder as it thinks there are multiple top level elements. 
16686  *
16687  * String Over-ride & Translations
16688  *
16689  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16690  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16691  * are needed. @see Roo.XComponent.overlayString  
16692  * 
16693  * 
16694  * 
16695  * @extends Roo.util.Observable
16696  * @constructor
16697  * @param cfg {Object} configuration of component
16698  * 
16699  */
16700 Roo.XComponent = function(cfg) {
16701     Roo.apply(this, cfg);
16702     this.addEvents({ 
16703         /**
16704              * @event built
16705              * Fires when this the componnt is built
16706              * @param {Roo.XComponent} c the component
16707              */
16708         'built' : true
16709         
16710     });
16711     this.region = this.region || 'center'; // default..
16712     Roo.XComponent.register(this);
16713     this.modules = false;
16714     this.el = false; // where the layout goes..
16715     
16716     
16717 }
16718 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16719     /**
16720      * @property el
16721      * The created element (with Roo.factory())
16722      * @type {Roo.Layout}
16723      */
16724     el  : false,
16725     
16726     /**
16727      * @property el
16728      * for BC  - use el in new code
16729      * @type {Roo.Layout}
16730      */
16731     panel : false,
16732     
16733     /**
16734      * @property layout
16735      * for BC  - use el in new code
16736      * @type {Roo.Layout}
16737      */
16738     layout : false,
16739     
16740      /**
16741      * @cfg {Function|boolean} disabled
16742      * If this module is disabled by some rule, return true from the funtion
16743      */
16744     disabled : false,
16745     
16746     /**
16747      * @cfg {String} parent 
16748      * Name of parent element which it get xtype added to..
16749      */
16750     parent: false,
16751     
16752     /**
16753      * @cfg {String} order
16754      * Used to set the order in which elements are created (usefull for multiple tabs)
16755      */
16756     
16757     order : false,
16758     /**
16759      * @cfg {String} name
16760      * String to display while loading.
16761      */
16762     name : false,
16763     /**
16764      * @cfg {String} region
16765      * Region to render component to (defaults to center)
16766      */
16767     region : 'center',
16768     
16769     /**
16770      * @cfg {Array} items
16771      * A single item array - the first element is the root of the tree..
16772      * It's done this way to stay compatible with the Xtype system...
16773      */
16774     items : false,
16775     
16776     /**
16777      * @property _tree
16778      * The method that retuns the tree of parts that make up this compoennt 
16779      * @type {function}
16780      */
16781     _tree  : false,
16782     
16783      /**
16784      * render
16785      * render element to dom or tree
16786      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16787      */
16788     
16789     render : function(el)
16790     {
16791         
16792         el = el || false;
16793         var hp = this.parent ? 1 : 0;
16794         Roo.debug &&  Roo.log(this);
16795         
16796         var tree = this._tree ? this._tree() : this.tree();
16797
16798         
16799         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16800             // if parent is a '#.....' string, then let's use that..
16801             var ename = this.parent.substr(1);
16802             this.parent = false;
16803             Roo.debug && Roo.log(ename);
16804             switch (ename) {
16805                 case 'bootstrap-body':
16806                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16807                         // this is the BorderLayout standard?
16808                        this.parent = { el : true };
16809                        break;
16810                     }
16811                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16812                         // need to insert stuff...
16813                         this.parent =  {
16814                              el : new Roo.bootstrap.layout.Border({
16815                                  el : document.body, 
16816                      
16817                                  center: {
16818                                     titlebar: false,
16819                                     autoScroll:false,
16820                                     closeOnTab: true,
16821                                     tabPosition: 'top',
16822                                       //resizeTabs: true,
16823                                     alwaysShowTabs: true,
16824                                     hideTabs: false
16825                                      //minTabWidth: 140
16826                                  }
16827                              })
16828                         
16829                          };
16830                          break;
16831                     }
16832                          
16833                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16834                         this.parent = { el :  new  Roo.bootstrap.Body() };
16835                         Roo.debug && Roo.log("setting el to doc body");
16836                          
16837                     } else {
16838                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16839                     }
16840                     break;
16841                 case 'bootstrap':
16842                     this.parent = { el : true};
16843                     // fall through
16844                 default:
16845                     el = Roo.get(ename);
16846                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16847                         this.parent = { el : true};
16848                     }
16849                     
16850                     break;
16851             }
16852                 
16853             
16854             if (!el && !this.parent) {
16855                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16856                 return;
16857             }
16858         }
16859         
16860         Roo.debug && Roo.log("EL:");
16861         Roo.debug && Roo.log(el);
16862         Roo.debug && Roo.log("this.parent.el:");
16863         Roo.debug && Roo.log(this.parent.el);
16864         
16865
16866         // altertive root elements ??? - we need a better way to indicate these.
16867         var is_alt = Roo.XComponent.is_alt ||
16868                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16869                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16870                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16871         
16872         
16873         
16874         if (!this.parent && is_alt) {
16875             //el = Roo.get(document.body);
16876             this.parent = { el : true };
16877         }
16878             
16879             
16880         
16881         if (!this.parent) {
16882             
16883             Roo.debug && Roo.log("no parent - creating one");
16884             
16885             el = el ? Roo.get(el) : false;      
16886             
16887             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16888                 
16889                 this.parent =  {
16890                     el : new Roo.bootstrap.layout.Border({
16891                         el: el || document.body,
16892                     
16893                         center: {
16894                             titlebar: false,
16895                             autoScroll:false,
16896                             closeOnTab: true,
16897                             tabPosition: 'top',
16898                              //resizeTabs: true,
16899                             alwaysShowTabs: false,
16900                             hideTabs: true,
16901                             minTabWidth: 140,
16902                             overflow: 'visible'
16903                          }
16904                      })
16905                 };
16906             } else {
16907             
16908                 // it's a top level one..
16909                 this.parent =  {
16910                     el : new Roo.BorderLayout(el || document.body, {
16911                         center: {
16912                             titlebar: false,
16913                             autoScroll:false,
16914                             closeOnTab: true,
16915                             tabPosition: 'top',
16916                              //resizeTabs: true,
16917                             alwaysShowTabs: el && hp? false :  true,
16918                             hideTabs: el || !hp ? true :  false,
16919                             minTabWidth: 140
16920                          }
16921                     })
16922                 };
16923             }
16924         }
16925         
16926         if (!this.parent.el) {
16927                 // probably an old style ctor, which has been disabled.
16928                 return;
16929
16930         }
16931                 // The 'tree' method is  '_tree now' 
16932             
16933         tree.region = tree.region || this.region;
16934         var is_body = false;
16935         if (this.parent.el === true) {
16936             // bootstrap... - body..
16937             if (el) {
16938                 tree.el = el;
16939             }
16940             this.parent.el = Roo.factory(tree);
16941             is_body = true;
16942         }
16943         
16944         this.el = this.parent.el.addxtype(tree, undefined, is_body);
16945         this.fireEvent('built', this);
16946         
16947         this.panel = this.el;
16948         this.layout = this.panel.layout;
16949         this.parentLayout = this.parent.layout  || false;  
16950          
16951     }
16952     
16953 });
16954
16955 Roo.apply(Roo.XComponent, {
16956     /**
16957      * @property  hideProgress
16958      * true to disable the building progress bar.. usefull on single page renders.
16959      * @type Boolean
16960      */
16961     hideProgress : false,
16962     /**
16963      * @property  buildCompleted
16964      * True when the builder has completed building the interface.
16965      * @type Boolean
16966      */
16967     buildCompleted : false,
16968      
16969     /**
16970      * @property  topModule
16971      * the upper most module - uses document.element as it's constructor.
16972      * @type Object
16973      */
16974      
16975     topModule  : false,
16976       
16977     /**
16978      * @property  modules
16979      * array of modules to be created by registration system.
16980      * @type {Array} of Roo.XComponent
16981      */
16982     
16983     modules : [],
16984     /**
16985      * @property  elmodules
16986      * array of modules to be created by which use #ID 
16987      * @type {Array} of Roo.XComponent
16988      */
16989      
16990     elmodules : [],
16991
16992      /**
16993      * @property  is_alt
16994      * Is an alternative Root - normally used by bootstrap or other systems,
16995      *    where the top element in the tree can wrap 'body' 
16996      * @type {boolean}  (default false)
16997      */
16998      
16999     is_alt : false,
17000     /**
17001      * @property  build_from_html
17002      * Build elements from html - used by bootstrap HTML stuff 
17003      *    - this is cleared after build is completed
17004      * @type {boolean}    (default false)
17005      */
17006      
17007     build_from_html : false,
17008     /**
17009      * Register components to be built later.
17010      *
17011      * This solves the following issues
17012      * - Building is not done on page load, but after an authentication process has occured.
17013      * - Interface elements are registered on page load
17014      * - Parent Interface elements may not be loaded before child, so this handles that..
17015      * 
17016      *
17017      * example:
17018      * 
17019      * MyApp.register({
17020           order : '000001',
17021           module : 'Pman.Tab.projectMgr',
17022           region : 'center',
17023           parent : 'Pman.layout',
17024           disabled : false,  // or use a function..
17025         })
17026      
17027      * * @param {Object} details about module
17028      */
17029     register : function(obj) {
17030                 
17031         Roo.XComponent.event.fireEvent('register', obj);
17032         switch(typeof(obj.disabled) ) {
17033                 
17034             case 'undefined':
17035                 break;
17036             
17037             case 'function':
17038                 if ( obj.disabled() ) {
17039                         return;
17040                 }
17041                 break;
17042             
17043             default:
17044                 if (obj.disabled || obj.region == '#disabled') {
17045                         return;
17046                 }
17047                 break;
17048         }
17049                 
17050         this.modules.push(obj);
17051          
17052     },
17053     /**
17054      * convert a string to an object..
17055      * eg. 'AAA.BBB' -> finds AAA.BBB
17056
17057      */
17058     
17059     toObject : function(str)
17060     {
17061         if (!str || typeof(str) == 'object') {
17062             return str;
17063         }
17064         if (str.substring(0,1) == '#') {
17065             return str;
17066         }
17067
17068         var ar = str.split('.');
17069         var rt, o;
17070         rt = ar.shift();
17071             /** eval:var:o */
17072         try {
17073             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17074         } catch (e) {
17075             throw "Module not found : " + str;
17076         }
17077         
17078         if (o === false) {
17079             throw "Module not found : " + str;
17080         }
17081         Roo.each(ar, function(e) {
17082             if (typeof(o[e]) == 'undefined') {
17083                 throw "Module not found : " + str;
17084             }
17085             o = o[e];
17086         });
17087         
17088         return o;
17089         
17090     },
17091     
17092     
17093     /**
17094      * move modules into their correct place in the tree..
17095      * 
17096      */
17097     preBuild : function ()
17098     {
17099         var _t = this;
17100         Roo.each(this.modules , function (obj)
17101         {
17102             Roo.XComponent.event.fireEvent('beforebuild', obj);
17103             
17104             var opar = obj.parent;
17105             try { 
17106                 obj.parent = this.toObject(opar);
17107             } catch(e) {
17108                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17109                 return;
17110             }
17111             
17112             if (!obj.parent) {
17113                 Roo.debug && Roo.log("GOT top level module");
17114                 Roo.debug && Roo.log(obj);
17115                 obj.modules = new Roo.util.MixedCollection(false, 
17116                     function(o) { return o.order + '' }
17117                 );
17118                 this.topModule = obj;
17119                 return;
17120             }
17121                         // parent is a string (usually a dom element name..)
17122             if (typeof(obj.parent) == 'string') {
17123                 this.elmodules.push(obj);
17124                 return;
17125             }
17126             if (obj.parent.constructor != Roo.XComponent) {
17127                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17128             }
17129             if (!obj.parent.modules) {
17130                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17131                     function(o) { return o.order + '' }
17132                 );
17133             }
17134             if (obj.parent.disabled) {
17135                 obj.disabled = true;
17136             }
17137             obj.parent.modules.add(obj);
17138         }, this);
17139     },
17140     
17141      /**
17142      * make a list of modules to build.
17143      * @return {Array} list of modules. 
17144      */ 
17145     
17146     buildOrder : function()
17147     {
17148         var _this = this;
17149         var cmp = function(a,b) {   
17150             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17151         };
17152         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17153             throw "No top level modules to build";
17154         }
17155         
17156         // make a flat list in order of modules to build.
17157         var mods = this.topModule ? [ this.topModule ] : [];
17158                 
17159         
17160         // elmodules (is a list of DOM based modules )
17161         Roo.each(this.elmodules, function(e) {
17162             mods.push(e);
17163             if (!this.topModule &&
17164                 typeof(e.parent) == 'string' &&
17165                 e.parent.substring(0,1) == '#' &&
17166                 Roo.get(e.parent.substr(1))
17167                ) {
17168                 
17169                 _this.topModule = e;
17170             }
17171             
17172         });
17173
17174         
17175         // add modules to their parents..
17176         var addMod = function(m) {
17177             Roo.debug && Roo.log("build Order: add: " + m.name);
17178                 
17179             mods.push(m);
17180             if (m.modules && !m.disabled) {
17181                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17182                 m.modules.keySort('ASC',  cmp );
17183                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17184     
17185                 m.modules.each(addMod);
17186             } else {
17187                 Roo.debug && Roo.log("build Order: no child modules");
17188             }
17189             // not sure if this is used any more..
17190             if (m.finalize) {
17191                 m.finalize.name = m.name + " (clean up) ";
17192                 mods.push(m.finalize);
17193             }
17194             
17195         }
17196         if (this.topModule && this.topModule.modules) { 
17197             this.topModule.modules.keySort('ASC',  cmp );
17198             this.topModule.modules.each(addMod);
17199         } 
17200         return mods;
17201     },
17202     
17203      /**
17204      * Build the registered modules.
17205      * @param {Object} parent element.
17206      * @param {Function} optional method to call after module has been added.
17207      * 
17208      */ 
17209    
17210     build : function(opts) 
17211     {
17212         
17213         if (typeof(opts) != 'undefined') {
17214             Roo.apply(this,opts);
17215         }
17216         
17217         this.preBuild();
17218         var mods = this.buildOrder();
17219       
17220         //this.allmods = mods;
17221         //Roo.debug && Roo.log(mods);
17222         //return;
17223         if (!mods.length) { // should not happen
17224             throw "NO modules!!!";
17225         }
17226         
17227         
17228         var msg = "Building Interface...";
17229         // flash it up as modal - so we store the mask!?
17230         if (!this.hideProgress && Roo.MessageBox) {
17231             Roo.MessageBox.show({ title: 'loading' });
17232             Roo.MessageBox.show({
17233                title: "Please wait...",
17234                msg: msg,
17235                width:450,
17236                progress:true,
17237                buttons : false,
17238                closable:false,
17239                modal: false
17240               
17241             });
17242         }
17243         var total = mods.length;
17244         
17245         var _this = this;
17246         var progressRun = function() {
17247             if (!mods.length) {
17248                 Roo.debug && Roo.log('hide?');
17249                 if (!this.hideProgress && Roo.MessageBox) {
17250                     Roo.MessageBox.hide();
17251                 }
17252                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17253                 
17254                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17255                 
17256                 // THE END...
17257                 return false;   
17258             }
17259             
17260             var m = mods.shift();
17261             
17262             
17263             Roo.debug && Roo.log(m);
17264             // not sure if this is supported any more.. - modules that are are just function
17265             if (typeof(m) == 'function') { 
17266                 m.call(this);
17267                 return progressRun.defer(10, _this);
17268             } 
17269             
17270             
17271             msg = "Building Interface " + (total  - mods.length) + 
17272                     " of " + total + 
17273                     (m.name ? (' - ' + m.name) : '');
17274                         Roo.debug && Roo.log(msg);
17275             if (!_this.hideProgress &&  Roo.MessageBox) { 
17276                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17277             }
17278             
17279          
17280             // is the module disabled?
17281             var disabled = (typeof(m.disabled) == 'function') ?
17282                 m.disabled.call(m.module.disabled) : m.disabled;    
17283             
17284             
17285             if (disabled) {
17286                 return progressRun(); // we do not update the display!
17287             }
17288             
17289             // now build 
17290             
17291                         
17292                         
17293             m.render();
17294             // it's 10 on top level, and 1 on others??? why...
17295             return progressRun.defer(10, _this);
17296              
17297         }
17298         progressRun.defer(1, _this);
17299      
17300         
17301         
17302     },
17303     /**
17304      * Overlay a set of modified strings onto a component
17305      * This is dependant on our builder exporting the strings and 'named strings' elements.
17306      * 
17307      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17308      * @param {Object} associative array of 'named' string and it's new value.
17309      * 
17310      */
17311         overlayStrings : function( component, strings )
17312     {
17313         if (typeof(component['_named_strings']) == 'undefined') {
17314             throw "ERROR: component does not have _named_strings";
17315         }
17316         for ( var k in strings ) {
17317             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17318             if (md !== false) {
17319                 component['_strings'][md] = strings[k];
17320             } else {
17321                 Roo.log('could not find named string: ' + k + ' in');
17322                 Roo.log(component);
17323             }
17324             
17325         }
17326         
17327     },
17328     
17329         
17330         /**
17331          * Event Object.
17332          *
17333          *
17334          */
17335         event: false, 
17336     /**
17337          * wrapper for event.on - aliased later..  
17338          * Typically use to register a event handler for register:
17339          *
17340          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17341          *
17342          */
17343     on : false
17344    
17345     
17346     
17347 });
17348
17349 Roo.XComponent.event = new Roo.util.Observable({
17350                 events : { 
17351                         /**
17352                          * @event register
17353                          * Fires when an Component is registered,
17354                          * set the disable property on the Component to stop registration.
17355                          * @param {Roo.XComponent} c the component being registerd.
17356                          * 
17357                          */
17358                         'register' : true,
17359             /**
17360                          * @event beforebuild
17361                          * Fires before each Component is built
17362                          * can be used to apply permissions.
17363                          * @param {Roo.XComponent} c the component being registerd.
17364                          * 
17365                          */
17366                         'beforebuild' : true,
17367                         /**
17368                          * @event buildcomplete
17369                          * Fires on the top level element when all elements have been built
17370                          * @param {Roo.XComponent} the top level component.
17371                          */
17372                         'buildcomplete' : true
17373                         
17374                 }
17375 });
17376
17377 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17378  //
17379  /**
17380  * marked - a markdown parser
17381  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17382  * https://github.com/chjj/marked
17383  */
17384
17385
17386 /**
17387  *
17388  * Roo.Markdown - is a very crude wrapper around marked..
17389  *
17390  * usage:
17391  * 
17392  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17393  * 
17394  * Note: move the sample code to the bottom of this
17395  * file before uncommenting it.
17396  *
17397  */
17398
17399 Roo.Markdown = {};
17400 Roo.Markdown.toHtml = function(text) {
17401     
17402     var c = new Roo.Markdown.marked.setOptions({
17403             renderer: new Roo.Markdown.marked.Renderer(),
17404             gfm: true,
17405             tables: true,
17406             breaks: false,
17407             pedantic: false,
17408             sanitize: false,
17409             smartLists: true,
17410             smartypants: false
17411           });
17412     // A FEW HACKS!!?
17413     
17414     text = text.replace(/\\\n/g,' ');
17415     return Roo.Markdown.marked(text);
17416 };
17417 //
17418 // converter
17419 //
17420 // Wraps all "globals" so that the only thing
17421 // exposed is makeHtml().
17422 //
17423 (function() {
17424     
17425      /**
17426          * eval:var:escape
17427          * eval:var:unescape
17428          * eval:var:replace
17429          */
17430       
17431     /**
17432      * Helpers
17433      */
17434     
17435     var escape = function (html, encode) {
17436       return html
17437         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17438         .replace(/</g, '&lt;')
17439         .replace(/>/g, '&gt;')
17440         .replace(/"/g, '&quot;')
17441         .replace(/'/g, '&#39;');
17442     }
17443     
17444     var unescape = function (html) {
17445         // explicitly match decimal, hex, and named HTML entities 
17446       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17447         n = n.toLowerCase();
17448         if (n === 'colon') { return ':'; }
17449         if (n.charAt(0) === '#') {
17450           return n.charAt(1) === 'x'
17451             ? String.fromCharCode(parseInt(n.substring(2), 16))
17452             : String.fromCharCode(+n.substring(1));
17453         }
17454         return '';
17455       });
17456     }
17457     
17458     var replace = function (regex, opt) {
17459       regex = regex.source;
17460       opt = opt || '';
17461       return function self(name, val) {
17462         if (!name) { return new RegExp(regex, opt); }
17463         val = val.source || val;
17464         val = val.replace(/(^|[^\[])\^/g, '$1');
17465         regex = regex.replace(name, val);
17466         return self;
17467       };
17468     }
17469
17470
17471          /**
17472          * eval:var:noop
17473     */
17474     var noop = function () {}
17475     noop.exec = noop;
17476     
17477          /**
17478          * eval:var:merge
17479     */
17480     var merge = function (obj) {
17481       var i = 1
17482         , target
17483         , key;
17484     
17485       for (; i < arguments.length; i++) {
17486         target = arguments[i];
17487         for (key in target) {
17488           if (Object.prototype.hasOwnProperty.call(target, key)) {
17489             obj[key] = target[key];
17490           }
17491         }
17492       }
17493     
17494       return obj;
17495     }
17496     
17497     
17498     /**
17499      * Block-Level Grammar
17500      */
17501     
17502     
17503     
17504     
17505     var block = {
17506       newline: /^\n+/,
17507       code: /^( {4}[^\n]+\n*)+/,
17508       fences: noop,
17509       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17510       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17511       nptable: noop,
17512       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17513       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17514       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17515       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17516       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17517       table: noop,
17518       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17519       text: /^[^\n]+/
17520     };
17521     
17522     block.bullet = /(?:[*+-]|\d+\.)/;
17523     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17524     block.item = replace(block.item, 'gm')
17525       (/bull/g, block.bullet)
17526       ();
17527     
17528     block.list = replace(block.list)
17529       (/bull/g, block.bullet)
17530       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17531       ('def', '\\n+(?=' + block.def.source + ')')
17532       ();
17533     
17534     block.blockquote = replace(block.blockquote)
17535       ('def', block.def)
17536       ();
17537     
17538     block._tag = '(?!(?:'
17539       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17540       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17541       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17542     
17543     block.html = replace(block.html)
17544       ('comment', /<!--[\s\S]*?-->/)
17545       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17546       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17547       (/tag/g, block._tag)
17548       ();
17549     
17550     block.paragraph = replace(block.paragraph)
17551       ('hr', block.hr)
17552       ('heading', block.heading)
17553       ('lheading', block.lheading)
17554       ('blockquote', block.blockquote)
17555       ('tag', '<' + block._tag)
17556       ('def', block.def)
17557       ();
17558     
17559     /**
17560      * Normal Block Grammar
17561      */
17562     
17563     block.normal = merge({}, block);
17564     
17565     /**
17566      * GFM Block Grammar
17567      */
17568     
17569     block.gfm = merge({}, block.normal, {
17570       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17571       paragraph: /^/,
17572       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17573     });
17574     
17575     block.gfm.paragraph = replace(block.paragraph)
17576       ('(?!', '(?!'
17577         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17578         + block.list.source.replace('\\1', '\\3') + '|')
17579       ();
17580     
17581     /**
17582      * GFM + Tables Block Grammar
17583      */
17584     
17585     block.tables = merge({}, block.gfm, {
17586       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17587       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17588     });
17589     
17590     /**
17591      * Block Lexer
17592      */
17593     
17594     var Lexer = function (options) {
17595       this.tokens = [];
17596       this.tokens.links = {};
17597       this.options = options || marked.defaults;
17598       this.rules = block.normal;
17599     
17600       if (this.options.gfm) {
17601         if (this.options.tables) {
17602           this.rules = block.tables;
17603         } else {
17604           this.rules = block.gfm;
17605         }
17606       }
17607     }
17608     
17609     /**
17610      * Expose Block Rules
17611      */
17612     
17613     Lexer.rules = block;
17614     
17615     /**
17616      * Static Lex Method
17617      */
17618     
17619     Lexer.lex = function(src, options) {
17620       var lexer = new Lexer(options);
17621       return lexer.lex(src);
17622     };
17623     
17624     /**
17625      * Preprocessing
17626      */
17627     
17628     Lexer.prototype.lex = function(src) {
17629       src = src
17630         .replace(/\r\n|\r/g, '\n')
17631         .replace(/\t/g, '    ')
17632         .replace(/\u00a0/g, ' ')
17633         .replace(/\u2424/g, '\n');
17634     
17635       return this.token(src, true);
17636     };
17637     
17638     /**
17639      * Lexing
17640      */
17641     
17642     Lexer.prototype.token = function(src, top, bq) {
17643       var src = src.replace(/^ +$/gm, '')
17644         , next
17645         , loose
17646         , cap
17647         , bull
17648         , b
17649         , item
17650         , space
17651         , i
17652         , l;
17653     
17654       while (src) {
17655         // newline
17656         if (cap = this.rules.newline.exec(src)) {
17657           src = src.substring(cap[0].length);
17658           if (cap[0].length > 1) {
17659             this.tokens.push({
17660               type: 'space'
17661             });
17662           }
17663         }
17664     
17665         // code
17666         if (cap = this.rules.code.exec(src)) {
17667           src = src.substring(cap[0].length);
17668           cap = cap[0].replace(/^ {4}/gm, '');
17669           this.tokens.push({
17670             type: 'code',
17671             text: !this.options.pedantic
17672               ? cap.replace(/\n+$/, '')
17673               : cap
17674           });
17675           continue;
17676         }
17677     
17678         // fences (gfm)
17679         if (cap = this.rules.fences.exec(src)) {
17680           src = src.substring(cap[0].length);
17681           this.tokens.push({
17682             type: 'code',
17683             lang: cap[2],
17684             text: cap[3] || ''
17685           });
17686           continue;
17687         }
17688     
17689         // heading
17690         if (cap = this.rules.heading.exec(src)) {
17691           src = src.substring(cap[0].length);
17692           this.tokens.push({
17693             type: 'heading',
17694             depth: cap[1].length,
17695             text: cap[2]
17696           });
17697           continue;
17698         }
17699     
17700         // table no leading pipe (gfm)
17701         if (top && (cap = this.rules.nptable.exec(src))) {
17702           src = src.substring(cap[0].length);
17703     
17704           item = {
17705             type: 'table',
17706             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17707             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17708             cells: cap[3].replace(/\n$/, '').split('\n')
17709           };
17710     
17711           for (i = 0; i < item.align.length; i++) {
17712             if (/^ *-+: *$/.test(item.align[i])) {
17713               item.align[i] = 'right';
17714             } else if (/^ *:-+: *$/.test(item.align[i])) {
17715               item.align[i] = 'center';
17716             } else if (/^ *:-+ *$/.test(item.align[i])) {
17717               item.align[i] = 'left';
17718             } else {
17719               item.align[i] = null;
17720             }
17721           }
17722     
17723           for (i = 0; i < item.cells.length; i++) {
17724             item.cells[i] = item.cells[i].split(/ *\| */);
17725           }
17726     
17727           this.tokens.push(item);
17728     
17729           continue;
17730         }
17731     
17732         // lheading
17733         if (cap = this.rules.lheading.exec(src)) {
17734           src = src.substring(cap[0].length);
17735           this.tokens.push({
17736             type: 'heading',
17737             depth: cap[2] === '=' ? 1 : 2,
17738             text: cap[1]
17739           });
17740           continue;
17741         }
17742     
17743         // hr
17744         if (cap = this.rules.hr.exec(src)) {
17745           src = src.substring(cap[0].length);
17746           this.tokens.push({
17747             type: 'hr'
17748           });
17749           continue;
17750         }
17751     
17752         // blockquote
17753         if (cap = this.rules.blockquote.exec(src)) {
17754           src = src.substring(cap[0].length);
17755     
17756           this.tokens.push({
17757             type: 'blockquote_start'
17758           });
17759     
17760           cap = cap[0].replace(/^ *> ?/gm, '');
17761     
17762           // Pass `top` to keep the current
17763           // "toplevel" state. This is exactly
17764           // how markdown.pl works.
17765           this.token(cap, top, true);
17766     
17767           this.tokens.push({
17768             type: 'blockquote_end'
17769           });
17770     
17771           continue;
17772         }
17773     
17774         // list
17775         if (cap = this.rules.list.exec(src)) {
17776           src = src.substring(cap[0].length);
17777           bull = cap[2];
17778     
17779           this.tokens.push({
17780             type: 'list_start',
17781             ordered: bull.length > 1
17782           });
17783     
17784           // Get each top-level item.
17785           cap = cap[0].match(this.rules.item);
17786     
17787           next = false;
17788           l = cap.length;
17789           i = 0;
17790     
17791           for (; i < l; i++) {
17792             item = cap[i];
17793     
17794             // Remove the list item's bullet
17795             // so it is seen as the next token.
17796             space = item.length;
17797             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17798     
17799             // Outdent whatever the
17800             // list item contains. Hacky.
17801             if (~item.indexOf('\n ')) {
17802               space -= item.length;
17803               item = !this.options.pedantic
17804                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17805                 : item.replace(/^ {1,4}/gm, '');
17806             }
17807     
17808             // Determine whether the next list item belongs here.
17809             // Backpedal if it does not belong in this list.
17810             if (this.options.smartLists && i !== l - 1) {
17811               b = block.bullet.exec(cap[i + 1])[0];
17812               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17813                 src = cap.slice(i + 1).join('\n') + src;
17814                 i = l - 1;
17815               }
17816             }
17817     
17818             // Determine whether item is loose or not.
17819             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17820             // for discount behavior.
17821             loose = next || /\n\n(?!\s*$)/.test(item);
17822             if (i !== l - 1) {
17823               next = item.charAt(item.length - 1) === '\n';
17824               if (!loose) { loose = next; }
17825             }
17826     
17827             this.tokens.push({
17828               type: loose
17829                 ? 'loose_item_start'
17830                 : 'list_item_start'
17831             });
17832     
17833             // Recurse.
17834             this.token(item, false, bq);
17835     
17836             this.tokens.push({
17837               type: 'list_item_end'
17838             });
17839           }
17840     
17841           this.tokens.push({
17842             type: 'list_end'
17843           });
17844     
17845           continue;
17846         }
17847     
17848         // html
17849         if (cap = this.rules.html.exec(src)) {
17850           src = src.substring(cap[0].length);
17851           this.tokens.push({
17852             type: this.options.sanitize
17853               ? 'paragraph'
17854               : 'html',
17855             pre: !this.options.sanitizer
17856               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17857             text: cap[0]
17858           });
17859           continue;
17860         }
17861     
17862         // def
17863         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17864           src = src.substring(cap[0].length);
17865           this.tokens.links[cap[1].toLowerCase()] = {
17866             href: cap[2],
17867             title: cap[3]
17868           };
17869           continue;
17870         }
17871     
17872         // table (gfm)
17873         if (top && (cap = this.rules.table.exec(src))) {
17874           src = src.substring(cap[0].length);
17875     
17876           item = {
17877             type: 'table',
17878             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17879             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17880             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17881           };
17882     
17883           for (i = 0; i < item.align.length; i++) {
17884             if (/^ *-+: *$/.test(item.align[i])) {
17885               item.align[i] = 'right';
17886             } else if (/^ *:-+: *$/.test(item.align[i])) {
17887               item.align[i] = 'center';
17888             } else if (/^ *:-+ *$/.test(item.align[i])) {
17889               item.align[i] = 'left';
17890             } else {
17891               item.align[i] = null;
17892             }
17893           }
17894     
17895           for (i = 0; i < item.cells.length; i++) {
17896             item.cells[i] = item.cells[i]
17897               .replace(/^ *\| *| *\| *$/g, '')
17898               .split(/ *\| */);
17899           }
17900     
17901           this.tokens.push(item);
17902     
17903           continue;
17904         }
17905     
17906         // top-level paragraph
17907         if (top && (cap = this.rules.paragraph.exec(src))) {
17908           src = src.substring(cap[0].length);
17909           this.tokens.push({
17910             type: 'paragraph',
17911             text: cap[1].charAt(cap[1].length - 1) === '\n'
17912               ? cap[1].slice(0, -1)
17913               : cap[1]
17914           });
17915           continue;
17916         }
17917     
17918         // text
17919         if (cap = this.rules.text.exec(src)) {
17920           // Top-level should never reach here.
17921           src = src.substring(cap[0].length);
17922           this.tokens.push({
17923             type: 'text',
17924             text: cap[0]
17925           });
17926           continue;
17927         }
17928     
17929         if (src) {
17930           throw new
17931             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17932         }
17933       }
17934     
17935       return this.tokens;
17936     };
17937     
17938     /**
17939      * Inline-Level Grammar
17940      */
17941     
17942     var inline = {
17943       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
17944       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
17945       url: noop,
17946       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
17947       link: /^!?\[(inside)\]\(href\)/,
17948       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
17949       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
17950       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
17951       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
17952       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
17953       br: /^ {2,}\n(?!\s*$)/,
17954       del: noop,
17955       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
17956     };
17957     
17958     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
17959     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
17960     
17961     inline.link = replace(inline.link)
17962       ('inside', inline._inside)
17963       ('href', inline._href)
17964       ();
17965     
17966     inline.reflink = replace(inline.reflink)
17967       ('inside', inline._inside)
17968       ();
17969     
17970     /**
17971      * Normal Inline Grammar
17972      */
17973     
17974     inline.normal = merge({}, inline);
17975     
17976     /**
17977      * Pedantic Inline Grammar
17978      */
17979     
17980     inline.pedantic = merge({}, inline.normal, {
17981       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
17982       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
17983     });
17984     
17985     /**
17986      * GFM Inline Grammar
17987      */
17988     
17989     inline.gfm = merge({}, inline.normal, {
17990       escape: replace(inline.escape)('])', '~|])')(),
17991       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
17992       del: /^~~(?=\S)([\s\S]*?\S)~~/,
17993       text: replace(inline.text)
17994         (']|', '~]|')
17995         ('|', '|https?://|')
17996         ()
17997     });
17998     
17999     /**
18000      * GFM + Line Breaks Inline Grammar
18001      */
18002     
18003     inline.breaks = merge({}, inline.gfm, {
18004       br: replace(inline.br)('{2,}', '*')(),
18005       text: replace(inline.gfm.text)('{2,}', '*')()
18006     });
18007     
18008     /**
18009      * Inline Lexer & Compiler
18010      */
18011     
18012     var InlineLexer  = function (links, options) {
18013       this.options = options || marked.defaults;
18014       this.links = links;
18015       this.rules = inline.normal;
18016       this.renderer = this.options.renderer || new Renderer;
18017       this.renderer.options = this.options;
18018     
18019       if (!this.links) {
18020         throw new
18021           Error('Tokens array requires a `links` property.');
18022       }
18023     
18024       if (this.options.gfm) {
18025         if (this.options.breaks) {
18026           this.rules = inline.breaks;
18027         } else {
18028           this.rules = inline.gfm;
18029         }
18030       } else if (this.options.pedantic) {
18031         this.rules = inline.pedantic;
18032       }
18033     }
18034     
18035     /**
18036      * Expose Inline Rules
18037      */
18038     
18039     InlineLexer.rules = inline;
18040     
18041     /**
18042      * Static Lexing/Compiling Method
18043      */
18044     
18045     InlineLexer.output = function(src, links, options) {
18046       var inline = new InlineLexer(links, options);
18047       return inline.output(src);
18048     };
18049     
18050     /**
18051      * Lexing/Compiling
18052      */
18053     
18054     InlineLexer.prototype.output = function(src) {
18055       var out = ''
18056         , link
18057         , text
18058         , href
18059         , cap;
18060     
18061       while (src) {
18062         // escape
18063         if (cap = this.rules.escape.exec(src)) {
18064           src = src.substring(cap[0].length);
18065           out += cap[1];
18066           continue;
18067         }
18068     
18069         // autolink
18070         if (cap = this.rules.autolink.exec(src)) {
18071           src = src.substring(cap[0].length);
18072           if (cap[2] === '@') {
18073             text = cap[1].charAt(6) === ':'
18074               ? this.mangle(cap[1].substring(7))
18075               : this.mangle(cap[1]);
18076             href = this.mangle('mailto:') + text;
18077           } else {
18078             text = escape(cap[1]);
18079             href = text;
18080           }
18081           out += this.renderer.link(href, null, text);
18082           continue;
18083         }
18084     
18085         // url (gfm)
18086         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18087           src = src.substring(cap[0].length);
18088           text = escape(cap[1]);
18089           href = text;
18090           out += this.renderer.link(href, null, text);
18091           continue;
18092         }
18093     
18094         // tag
18095         if (cap = this.rules.tag.exec(src)) {
18096           if (!this.inLink && /^<a /i.test(cap[0])) {
18097             this.inLink = true;
18098           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18099             this.inLink = false;
18100           }
18101           src = src.substring(cap[0].length);
18102           out += this.options.sanitize
18103             ? this.options.sanitizer
18104               ? this.options.sanitizer(cap[0])
18105               : escape(cap[0])
18106             : cap[0];
18107           continue;
18108         }
18109     
18110         // link
18111         if (cap = this.rules.link.exec(src)) {
18112           src = src.substring(cap[0].length);
18113           this.inLink = true;
18114           out += this.outputLink(cap, {
18115             href: cap[2],
18116             title: cap[3]
18117           });
18118           this.inLink = false;
18119           continue;
18120         }
18121     
18122         // reflink, nolink
18123         if ((cap = this.rules.reflink.exec(src))
18124             || (cap = this.rules.nolink.exec(src))) {
18125           src = src.substring(cap[0].length);
18126           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18127           link = this.links[link.toLowerCase()];
18128           if (!link || !link.href) {
18129             out += cap[0].charAt(0);
18130             src = cap[0].substring(1) + src;
18131             continue;
18132           }
18133           this.inLink = true;
18134           out += this.outputLink(cap, link);
18135           this.inLink = false;
18136           continue;
18137         }
18138     
18139         // strong
18140         if (cap = this.rules.strong.exec(src)) {
18141           src = src.substring(cap[0].length);
18142           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18143           continue;
18144         }
18145     
18146         // em
18147         if (cap = this.rules.em.exec(src)) {
18148           src = src.substring(cap[0].length);
18149           out += this.renderer.em(this.output(cap[2] || cap[1]));
18150           continue;
18151         }
18152     
18153         // code
18154         if (cap = this.rules.code.exec(src)) {
18155           src = src.substring(cap[0].length);
18156           out += this.renderer.codespan(escape(cap[2], true));
18157           continue;
18158         }
18159     
18160         // br
18161         if (cap = this.rules.br.exec(src)) {
18162           src = src.substring(cap[0].length);
18163           out += this.renderer.br();
18164           continue;
18165         }
18166     
18167         // del (gfm)
18168         if (cap = this.rules.del.exec(src)) {
18169           src = src.substring(cap[0].length);
18170           out += this.renderer.del(this.output(cap[1]));
18171           continue;
18172         }
18173     
18174         // text
18175         if (cap = this.rules.text.exec(src)) {
18176           src = src.substring(cap[0].length);
18177           out += this.renderer.text(escape(this.smartypants(cap[0])));
18178           continue;
18179         }
18180     
18181         if (src) {
18182           throw new
18183             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18184         }
18185       }
18186     
18187       return out;
18188     };
18189     
18190     /**
18191      * Compile Link
18192      */
18193     
18194     InlineLexer.prototype.outputLink = function(cap, link) {
18195       var href = escape(link.href)
18196         , title = link.title ? escape(link.title) : null;
18197     
18198       return cap[0].charAt(0) !== '!'
18199         ? this.renderer.link(href, title, this.output(cap[1]))
18200         : this.renderer.image(href, title, escape(cap[1]));
18201     };
18202     
18203     /**
18204      * Smartypants Transformations
18205      */
18206     
18207     InlineLexer.prototype.smartypants = function(text) {
18208       if (!this.options.smartypants)  { return text; }
18209       return text
18210         // em-dashes
18211         .replace(/---/g, '\u2014')
18212         // en-dashes
18213         .replace(/--/g, '\u2013')
18214         // opening singles
18215         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18216         // closing singles & apostrophes
18217         .replace(/'/g, '\u2019')
18218         // opening doubles
18219         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18220         // closing doubles
18221         .replace(/"/g, '\u201d')
18222         // ellipses
18223         .replace(/\.{3}/g, '\u2026');
18224     };
18225     
18226     /**
18227      * Mangle Links
18228      */
18229     
18230     InlineLexer.prototype.mangle = function(text) {
18231       if (!this.options.mangle) { return text; }
18232       var out = ''
18233         , l = text.length
18234         , i = 0
18235         , ch;
18236     
18237       for (; i < l; i++) {
18238         ch = text.charCodeAt(i);
18239         if (Math.random() > 0.5) {
18240           ch = 'x' + ch.toString(16);
18241         }
18242         out += '&#' + ch + ';';
18243       }
18244     
18245       return out;
18246     };
18247     
18248     /**
18249      * Renderer
18250      */
18251     
18252      /**
18253          * eval:var:Renderer
18254     */
18255     
18256     var Renderer   = function (options) {
18257       this.options = options || {};
18258     }
18259     
18260     Renderer.prototype.code = function(code, lang, escaped) {
18261       if (this.options.highlight) {
18262         var out = this.options.highlight(code, lang);
18263         if (out != null && out !== code) {
18264           escaped = true;
18265           code = out;
18266         }
18267       } else {
18268             // hack!!! - it's already escapeD?
18269             escaped = true;
18270       }
18271     
18272       if (!lang) {
18273         return '<pre><code>'
18274           + (escaped ? code : escape(code, true))
18275           + '\n</code></pre>';
18276       }
18277     
18278       return '<pre><code class="'
18279         + this.options.langPrefix
18280         + escape(lang, true)
18281         + '">'
18282         + (escaped ? code : escape(code, true))
18283         + '\n</code></pre>\n';
18284     };
18285     
18286     Renderer.prototype.blockquote = function(quote) {
18287       return '<blockquote>\n' + quote + '</blockquote>\n';
18288     };
18289     
18290     Renderer.prototype.html = function(html) {
18291       return html;
18292     };
18293     
18294     Renderer.prototype.heading = function(text, level, raw) {
18295       return '<h'
18296         + level
18297         + ' id="'
18298         + this.options.headerPrefix
18299         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18300         + '">'
18301         + text
18302         + '</h'
18303         + level
18304         + '>\n';
18305     };
18306     
18307     Renderer.prototype.hr = function() {
18308       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18309     };
18310     
18311     Renderer.prototype.list = function(body, ordered) {
18312       var type = ordered ? 'ol' : 'ul';
18313       return '<' + type + '>\n' + body + '</' + type + '>\n';
18314     };
18315     
18316     Renderer.prototype.listitem = function(text) {
18317       return '<li>' + text + '</li>\n';
18318     };
18319     
18320     Renderer.prototype.paragraph = function(text) {
18321       return '<p>' + text + '</p>\n';
18322     };
18323     
18324     Renderer.prototype.table = function(header, body) {
18325       return '<table class="table table-striped">\n'
18326         + '<thead>\n'
18327         + header
18328         + '</thead>\n'
18329         + '<tbody>\n'
18330         + body
18331         + '</tbody>\n'
18332         + '</table>\n';
18333     };
18334     
18335     Renderer.prototype.tablerow = function(content) {
18336       return '<tr>\n' + content + '</tr>\n';
18337     };
18338     
18339     Renderer.prototype.tablecell = function(content, flags) {
18340       var type = flags.header ? 'th' : 'td';
18341       var tag = flags.align
18342         ? '<' + type + ' style="text-align:' + flags.align + '">'
18343         : '<' + type + '>';
18344       return tag + content + '</' + type + '>\n';
18345     };
18346     
18347     // span level renderer
18348     Renderer.prototype.strong = function(text) {
18349       return '<strong>' + text + '</strong>';
18350     };
18351     
18352     Renderer.prototype.em = function(text) {
18353       return '<em>' + text + '</em>';
18354     };
18355     
18356     Renderer.prototype.codespan = function(text) {
18357       return '<code>' + text + '</code>';
18358     };
18359     
18360     Renderer.prototype.br = function() {
18361       return this.options.xhtml ? '<br/>' : '<br>';
18362     };
18363     
18364     Renderer.prototype.del = function(text) {
18365       return '<del>' + text + '</del>';
18366     };
18367     
18368     Renderer.prototype.link = function(href, title, text) {
18369       if (this.options.sanitize) {
18370         try {
18371           var prot = decodeURIComponent(unescape(href))
18372             .replace(/[^\w:]/g, '')
18373             .toLowerCase();
18374         } catch (e) {
18375           return '';
18376         }
18377         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18378           return '';
18379         }
18380       }
18381       var out = '<a href="' + href + '"';
18382       if (title) {
18383         out += ' title="' + title + '"';
18384       }
18385       out += '>' + text + '</a>';
18386       return out;
18387     };
18388     
18389     Renderer.prototype.image = function(href, title, text) {
18390       var out = '<img src="' + href + '" alt="' + text + '"';
18391       if (title) {
18392         out += ' title="' + title + '"';
18393       }
18394       out += this.options.xhtml ? '/>' : '>';
18395       return out;
18396     };
18397     
18398     Renderer.prototype.text = function(text) {
18399       return text;
18400     };
18401     
18402     /**
18403      * Parsing & Compiling
18404      */
18405          /**
18406          * eval:var:Parser
18407     */
18408     
18409     var Parser= function (options) {
18410       this.tokens = [];
18411       this.token = null;
18412       this.options = options || marked.defaults;
18413       this.options.renderer = this.options.renderer || new Renderer;
18414       this.renderer = this.options.renderer;
18415       this.renderer.options = this.options;
18416     }
18417     
18418     /**
18419      * Static Parse Method
18420      */
18421     
18422     Parser.parse = function(src, options, renderer) {
18423       var parser = new Parser(options, renderer);
18424       return parser.parse(src);
18425     };
18426     
18427     /**
18428      * Parse Loop
18429      */
18430     
18431     Parser.prototype.parse = function(src) {
18432       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18433       this.tokens = src.reverse();
18434     
18435       var out = '';
18436       while (this.next()) {
18437         out += this.tok();
18438       }
18439     
18440       return out;
18441     };
18442     
18443     /**
18444      * Next Token
18445      */
18446     
18447     Parser.prototype.next = function() {
18448       return this.token = this.tokens.pop();
18449     };
18450     
18451     /**
18452      * Preview Next Token
18453      */
18454     
18455     Parser.prototype.peek = function() {
18456       return this.tokens[this.tokens.length - 1] || 0;
18457     };
18458     
18459     /**
18460      * Parse Text Tokens
18461      */
18462     
18463     Parser.prototype.parseText = function() {
18464       var body = this.token.text;
18465     
18466       while (this.peek().type === 'text') {
18467         body += '\n' + this.next().text;
18468       }
18469     
18470       return this.inline.output(body);
18471     };
18472     
18473     /**
18474      * Parse Current Token
18475      */
18476     
18477     Parser.prototype.tok = function() {
18478       switch (this.token.type) {
18479         case 'space': {
18480           return '';
18481         }
18482         case 'hr': {
18483           return this.renderer.hr();
18484         }
18485         case 'heading': {
18486           return this.renderer.heading(
18487             this.inline.output(this.token.text),
18488             this.token.depth,
18489             this.token.text);
18490         }
18491         case 'code': {
18492           return this.renderer.code(this.token.text,
18493             this.token.lang,
18494             this.token.escaped);
18495         }
18496         case 'table': {
18497           var header = ''
18498             , body = ''
18499             , i
18500             , row
18501             , cell
18502             , flags
18503             , j;
18504     
18505           // header
18506           cell = '';
18507           for (i = 0; i < this.token.header.length; i++) {
18508             flags = { header: true, align: this.token.align[i] };
18509             cell += this.renderer.tablecell(
18510               this.inline.output(this.token.header[i]),
18511               { header: true, align: this.token.align[i] }
18512             );
18513           }
18514           header += this.renderer.tablerow(cell);
18515     
18516           for (i = 0; i < this.token.cells.length; i++) {
18517             row = this.token.cells[i];
18518     
18519             cell = '';
18520             for (j = 0; j < row.length; j++) {
18521               cell += this.renderer.tablecell(
18522                 this.inline.output(row[j]),
18523                 { header: false, align: this.token.align[j] }
18524               );
18525             }
18526     
18527             body += this.renderer.tablerow(cell);
18528           }
18529           return this.renderer.table(header, body);
18530         }
18531         case 'blockquote_start': {
18532           var body = '';
18533     
18534           while (this.next().type !== 'blockquote_end') {
18535             body += this.tok();
18536           }
18537     
18538           return this.renderer.blockquote(body);
18539         }
18540         case 'list_start': {
18541           var body = ''
18542             , ordered = this.token.ordered;
18543     
18544           while (this.next().type !== 'list_end') {
18545             body += this.tok();
18546           }
18547     
18548           return this.renderer.list(body, ordered);
18549         }
18550         case 'list_item_start': {
18551           var body = '';
18552     
18553           while (this.next().type !== 'list_item_end') {
18554             body += this.token.type === 'text'
18555               ? this.parseText()
18556               : this.tok();
18557           }
18558     
18559           return this.renderer.listitem(body);
18560         }
18561         case 'loose_item_start': {
18562           var body = '';
18563     
18564           while (this.next().type !== 'list_item_end') {
18565             body += this.tok();
18566           }
18567     
18568           return this.renderer.listitem(body);
18569         }
18570         case 'html': {
18571           var html = !this.token.pre && !this.options.pedantic
18572             ? this.inline.output(this.token.text)
18573             : this.token.text;
18574           return this.renderer.html(html);
18575         }
18576         case 'paragraph': {
18577           return this.renderer.paragraph(this.inline.output(this.token.text));
18578         }
18579         case 'text': {
18580           return this.renderer.paragraph(this.parseText());
18581         }
18582       }
18583     };
18584   
18585     
18586     /**
18587      * Marked
18588      */
18589          /**
18590          * eval:var:marked
18591     */
18592     var marked = function (src, opt, callback) {
18593       if (callback || typeof opt === 'function') {
18594         if (!callback) {
18595           callback = opt;
18596           opt = null;
18597         }
18598     
18599         opt = merge({}, marked.defaults, opt || {});
18600     
18601         var highlight = opt.highlight
18602           , tokens
18603           , pending
18604           , i = 0;
18605     
18606         try {
18607           tokens = Lexer.lex(src, opt)
18608         } catch (e) {
18609           return callback(e);
18610         }
18611     
18612         pending = tokens.length;
18613          /**
18614          * eval:var:done
18615     */
18616         var done = function(err) {
18617           if (err) {
18618             opt.highlight = highlight;
18619             return callback(err);
18620           }
18621     
18622           var out;
18623     
18624           try {
18625             out = Parser.parse(tokens, opt);
18626           } catch (e) {
18627             err = e;
18628           }
18629     
18630           opt.highlight = highlight;
18631     
18632           return err
18633             ? callback(err)
18634             : callback(null, out);
18635         };
18636     
18637         if (!highlight || highlight.length < 3) {
18638           return done();
18639         }
18640     
18641         delete opt.highlight;
18642     
18643         if (!pending) { return done(); }
18644     
18645         for (; i < tokens.length; i++) {
18646           (function(token) {
18647             if (token.type !== 'code') {
18648               return --pending || done();
18649             }
18650             return highlight(token.text, token.lang, function(err, code) {
18651               if (err) { return done(err); }
18652               if (code == null || code === token.text) {
18653                 return --pending || done();
18654               }
18655               token.text = code;
18656               token.escaped = true;
18657               --pending || done();
18658             });
18659           })(tokens[i]);
18660         }
18661     
18662         return;
18663       }
18664       try {
18665         if (opt) { opt = merge({}, marked.defaults, opt); }
18666         return Parser.parse(Lexer.lex(src, opt), opt);
18667       } catch (e) {
18668         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18669         if ((opt || marked.defaults).silent) {
18670           return '<p>An error occured:</p><pre>'
18671             + escape(e.message + '', true)
18672             + '</pre>';
18673         }
18674         throw e;
18675       }
18676     }
18677     
18678     /**
18679      * Options
18680      */
18681     
18682     marked.options =
18683     marked.setOptions = function(opt) {
18684       merge(marked.defaults, opt);
18685       return marked;
18686     };
18687     
18688     marked.defaults = {
18689       gfm: true,
18690       tables: true,
18691       breaks: false,
18692       pedantic: false,
18693       sanitize: false,
18694       sanitizer: null,
18695       mangle: true,
18696       smartLists: false,
18697       silent: false,
18698       highlight: null,
18699       langPrefix: 'lang-',
18700       smartypants: false,
18701       headerPrefix: '',
18702       renderer: new Renderer,
18703       xhtml: false
18704     };
18705     
18706     /**
18707      * Expose
18708      */
18709     
18710     marked.Parser = Parser;
18711     marked.parser = Parser.parse;
18712     
18713     marked.Renderer = Renderer;
18714     
18715     marked.Lexer = Lexer;
18716     marked.lexer = Lexer.lex;
18717     
18718     marked.InlineLexer = InlineLexer;
18719     marked.inlineLexer = InlineLexer.output;
18720     
18721     marked.parse = marked;
18722     
18723     Roo.Markdown.marked = marked;
18724
18725 })();/*
18726  * Based on:
18727  * Ext JS Library 1.1.1
18728  * Copyright(c) 2006-2007, Ext JS, LLC.
18729  *
18730  * Originally Released Under LGPL - original licence link has changed is not relivant.
18731  *
18732  * Fork - LGPL
18733  * <script type="text/javascript">
18734  */
18735
18736
18737
18738 /*
18739  * These classes are derivatives of the similarly named classes in the YUI Library.
18740  * The original license:
18741  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18742  * Code licensed under the BSD License:
18743  * http://developer.yahoo.net/yui/license.txt
18744  */
18745
18746 (function() {
18747
18748 var Event=Roo.EventManager;
18749 var Dom=Roo.lib.Dom;
18750
18751 /**
18752  * @class Roo.dd.DragDrop
18753  * @extends Roo.util.Observable
18754  * Defines the interface and base operation of items that that can be
18755  * dragged or can be drop targets.  It was designed to be extended, overriding
18756  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18757  * Up to three html elements can be associated with a DragDrop instance:
18758  * <ul>
18759  * <li>linked element: the element that is passed into the constructor.
18760  * This is the element which defines the boundaries for interaction with
18761  * other DragDrop objects.</li>
18762  * <li>handle element(s): The drag operation only occurs if the element that
18763  * was clicked matches a handle element.  By default this is the linked
18764  * element, but there are times that you will want only a portion of the
18765  * linked element to initiate the drag operation, and the setHandleElId()
18766  * method provides a way to define this.</li>
18767  * <li>drag element: this represents the element that would be moved along
18768  * with the cursor during a drag operation.  By default, this is the linked
18769  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18770  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18771  * </li>
18772  * </ul>
18773  * This class should not be instantiated until the onload event to ensure that
18774  * the associated elements are available.
18775  * The following would define a DragDrop obj that would interact with any
18776  * other DragDrop obj in the "group1" group:
18777  * <pre>
18778  *  dd = new Roo.dd.DragDrop("div1", "group1");
18779  * </pre>
18780  * Since none of the event handlers have been implemented, nothing would
18781  * actually happen if you were to run the code above.  Normally you would
18782  * override this class or one of the default implementations, but you can
18783  * also override the methods you want on an instance of the class...
18784  * <pre>
18785  *  dd.onDragDrop = function(e, id) {
18786  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18787  *  }
18788  * </pre>
18789  * @constructor
18790  * @param {String} id of the element that is linked to this instance
18791  * @param {String} sGroup the group of related DragDrop objects
18792  * @param {object} config an object containing configurable attributes
18793  *                Valid properties for DragDrop:
18794  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18795  */
18796 Roo.dd.DragDrop = function(id, sGroup, config) {
18797     if (id) {
18798         this.init(id, sGroup, config);
18799     }
18800     
18801 };
18802
18803 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18804
18805     /**
18806      * The id of the element associated with this object.  This is what we
18807      * refer to as the "linked element" because the size and position of
18808      * this element is used to determine when the drag and drop objects have
18809      * interacted.
18810      * @property id
18811      * @type String
18812      */
18813     id: null,
18814
18815     /**
18816      * Configuration attributes passed into the constructor
18817      * @property config
18818      * @type object
18819      */
18820     config: null,
18821
18822     /**
18823      * The id of the element that will be dragged.  By default this is same
18824      * as the linked element , but could be changed to another element. Ex:
18825      * Roo.dd.DDProxy
18826      * @property dragElId
18827      * @type String
18828      * @private
18829      */
18830     dragElId: null,
18831
18832     /**
18833      * the id of the element that initiates the drag operation.  By default
18834      * this is the linked element, but could be changed to be a child of this
18835      * element.  This lets us do things like only starting the drag when the
18836      * header element within the linked html element is clicked.
18837      * @property handleElId
18838      * @type String
18839      * @private
18840      */
18841     handleElId: null,
18842
18843     /**
18844      * An associative array of HTML tags that will be ignored if clicked.
18845      * @property invalidHandleTypes
18846      * @type {string: string}
18847      */
18848     invalidHandleTypes: null,
18849
18850     /**
18851      * An associative array of ids for elements that will be ignored if clicked
18852      * @property invalidHandleIds
18853      * @type {string: string}
18854      */
18855     invalidHandleIds: null,
18856
18857     /**
18858      * An indexted array of css class names for elements that will be ignored
18859      * if clicked.
18860      * @property invalidHandleClasses
18861      * @type string[]
18862      */
18863     invalidHandleClasses: null,
18864
18865     /**
18866      * The linked element's absolute X position at the time the drag was
18867      * started
18868      * @property startPageX
18869      * @type int
18870      * @private
18871      */
18872     startPageX: 0,
18873
18874     /**
18875      * The linked element's absolute X position at the time the drag was
18876      * started
18877      * @property startPageY
18878      * @type int
18879      * @private
18880      */
18881     startPageY: 0,
18882
18883     /**
18884      * The group defines a logical collection of DragDrop objects that are
18885      * related.  Instances only get events when interacting with other
18886      * DragDrop object in the same group.  This lets us define multiple
18887      * groups using a single DragDrop subclass if we want.
18888      * @property groups
18889      * @type {string: string}
18890      */
18891     groups: null,
18892
18893     /**
18894      * Individual drag/drop instances can be locked.  This will prevent
18895      * onmousedown start drag.
18896      * @property locked
18897      * @type boolean
18898      * @private
18899      */
18900     locked: false,
18901
18902     /**
18903      * Lock this instance
18904      * @method lock
18905      */
18906     lock: function() { this.locked = true; },
18907
18908     /**
18909      * Unlock this instace
18910      * @method unlock
18911      */
18912     unlock: function() { this.locked = false; },
18913
18914     /**
18915      * By default, all insances can be a drop target.  This can be disabled by
18916      * setting isTarget to false.
18917      * @method isTarget
18918      * @type boolean
18919      */
18920     isTarget: true,
18921
18922     /**
18923      * The padding configured for this drag and drop object for calculating
18924      * the drop zone intersection with this object.
18925      * @method padding
18926      * @type int[]
18927      */
18928     padding: null,
18929
18930     /**
18931      * Cached reference to the linked element
18932      * @property _domRef
18933      * @private
18934      */
18935     _domRef: null,
18936
18937     /**
18938      * Internal typeof flag
18939      * @property __ygDragDrop
18940      * @private
18941      */
18942     __ygDragDrop: true,
18943
18944     /**
18945      * Set to true when horizontal contraints are applied
18946      * @property constrainX
18947      * @type boolean
18948      * @private
18949      */
18950     constrainX: false,
18951
18952     /**
18953      * Set to true when vertical contraints are applied
18954      * @property constrainY
18955      * @type boolean
18956      * @private
18957      */
18958     constrainY: false,
18959
18960     /**
18961      * The left constraint
18962      * @property minX
18963      * @type int
18964      * @private
18965      */
18966     minX: 0,
18967
18968     /**
18969      * The right constraint
18970      * @property maxX
18971      * @type int
18972      * @private
18973      */
18974     maxX: 0,
18975
18976     /**
18977      * The up constraint
18978      * @property minY
18979      * @type int
18980      * @type int
18981      * @private
18982      */
18983     minY: 0,
18984
18985     /**
18986      * The down constraint
18987      * @property maxY
18988      * @type int
18989      * @private
18990      */
18991     maxY: 0,
18992
18993     /**
18994      * Maintain offsets when we resetconstraints.  Set to true when you want
18995      * the position of the element relative to its parent to stay the same
18996      * when the page changes
18997      *
18998      * @property maintainOffset
18999      * @type boolean
19000      */
19001     maintainOffset: false,
19002
19003     /**
19004      * Array of pixel locations the element will snap to if we specified a
19005      * horizontal graduation/interval.  This array is generated automatically
19006      * when you define a tick interval.
19007      * @property xTicks
19008      * @type int[]
19009      */
19010     xTicks: null,
19011
19012     /**
19013      * Array of pixel locations the element will snap to if we specified a
19014      * vertical graduation/interval.  This array is generated automatically
19015      * when you define a tick interval.
19016      * @property yTicks
19017      * @type int[]
19018      */
19019     yTicks: null,
19020
19021     /**
19022      * By default the drag and drop instance will only respond to the primary
19023      * button click (left button for a right-handed mouse).  Set to true to
19024      * allow drag and drop to start with any mouse click that is propogated
19025      * by the browser
19026      * @property primaryButtonOnly
19027      * @type boolean
19028      */
19029     primaryButtonOnly: true,
19030
19031     /**
19032      * The availabe property is false until the linked dom element is accessible.
19033      * @property available
19034      * @type boolean
19035      */
19036     available: false,
19037
19038     /**
19039      * By default, drags can only be initiated if the mousedown occurs in the
19040      * region the linked element is.  This is done in part to work around a
19041      * bug in some browsers that mis-report the mousedown if the previous
19042      * mouseup happened outside of the window.  This property is set to true
19043      * if outer handles are defined.
19044      *
19045      * @property hasOuterHandles
19046      * @type boolean
19047      * @default false
19048      */
19049     hasOuterHandles: false,
19050
19051     /**
19052      * Code that executes immediately before the startDrag event
19053      * @method b4StartDrag
19054      * @private
19055      */
19056     b4StartDrag: function(x, y) { },
19057
19058     /**
19059      * Abstract method called after a drag/drop object is clicked
19060      * and the drag or mousedown time thresholds have beeen met.
19061      * @method startDrag
19062      * @param {int} X click location
19063      * @param {int} Y click location
19064      */
19065     startDrag: function(x, y) { /* override this */ },
19066
19067     /**
19068      * Code that executes immediately before the onDrag event
19069      * @method b4Drag
19070      * @private
19071      */
19072     b4Drag: function(e) { },
19073
19074     /**
19075      * Abstract method called during the onMouseMove event while dragging an
19076      * object.
19077      * @method onDrag
19078      * @param {Event} e the mousemove event
19079      */
19080     onDrag: function(e) { /* override this */ },
19081
19082     /**
19083      * Abstract method called when this element fist begins hovering over
19084      * another DragDrop obj
19085      * @method onDragEnter
19086      * @param {Event} e the mousemove event
19087      * @param {String|DragDrop[]} id In POINT mode, the element
19088      * id this is hovering over.  In INTERSECT mode, an array of one or more
19089      * dragdrop items being hovered over.
19090      */
19091     onDragEnter: function(e, id) { /* override this */ },
19092
19093     /**
19094      * Code that executes immediately before the onDragOver event
19095      * @method b4DragOver
19096      * @private
19097      */
19098     b4DragOver: function(e) { },
19099
19100     /**
19101      * Abstract method called when this element is hovering over another
19102      * DragDrop obj
19103      * @method onDragOver
19104      * @param {Event} e the mousemove event
19105      * @param {String|DragDrop[]} id In POINT mode, the element
19106      * id this is hovering over.  In INTERSECT mode, an array of dd items
19107      * being hovered over.
19108      */
19109     onDragOver: function(e, id) { /* override this */ },
19110
19111     /**
19112      * Code that executes immediately before the onDragOut event
19113      * @method b4DragOut
19114      * @private
19115      */
19116     b4DragOut: function(e) { },
19117
19118     /**
19119      * Abstract method called when we are no longer hovering over an element
19120      * @method onDragOut
19121      * @param {Event} e the mousemove event
19122      * @param {String|DragDrop[]} id In POINT mode, the element
19123      * id this was hovering over.  In INTERSECT mode, an array of dd items
19124      * that the mouse is no longer over.
19125      */
19126     onDragOut: function(e, id) { /* override this */ },
19127
19128     /**
19129      * Code that executes immediately before the onDragDrop event
19130      * @method b4DragDrop
19131      * @private
19132      */
19133     b4DragDrop: function(e) { },
19134
19135     /**
19136      * Abstract method called when this item is dropped on another DragDrop
19137      * obj
19138      * @method onDragDrop
19139      * @param {Event} e the mouseup event
19140      * @param {String|DragDrop[]} id In POINT mode, the element
19141      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19142      * was dropped on.
19143      */
19144     onDragDrop: function(e, id) { /* override this */ },
19145
19146     /**
19147      * Abstract method called when this item is dropped on an area with no
19148      * drop target
19149      * @method onInvalidDrop
19150      * @param {Event} e the mouseup event
19151      */
19152     onInvalidDrop: function(e) { /* override this */ },
19153
19154     /**
19155      * Code that executes immediately before the endDrag event
19156      * @method b4EndDrag
19157      * @private
19158      */
19159     b4EndDrag: function(e) { },
19160
19161     /**
19162      * Fired when we are done dragging the object
19163      * @method endDrag
19164      * @param {Event} e the mouseup event
19165      */
19166     endDrag: function(e) { /* override this */ },
19167
19168     /**
19169      * Code executed immediately before the onMouseDown event
19170      * @method b4MouseDown
19171      * @param {Event} e the mousedown event
19172      * @private
19173      */
19174     b4MouseDown: function(e) {  },
19175
19176     /**
19177      * Event handler that fires when a drag/drop obj gets a mousedown
19178      * @method onMouseDown
19179      * @param {Event} e the mousedown event
19180      */
19181     onMouseDown: function(e) { /* override this */ },
19182
19183     /**
19184      * Event handler that fires when a drag/drop obj gets a mouseup
19185      * @method onMouseUp
19186      * @param {Event} e the mouseup event
19187      */
19188     onMouseUp: function(e) { /* override this */ },
19189
19190     /**
19191      * Override the onAvailable method to do what is needed after the initial
19192      * position was determined.
19193      * @method onAvailable
19194      */
19195     onAvailable: function () {
19196     },
19197
19198     /*
19199      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19200      * @type Object
19201      */
19202     defaultPadding : {left:0, right:0, top:0, bottom:0},
19203
19204     /*
19205      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19206  *
19207  * Usage:
19208  <pre><code>
19209  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19210                 { dragElId: "existingProxyDiv" });
19211  dd.startDrag = function(){
19212      this.constrainTo("parent-id");
19213  };
19214  </code></pre>
19215  * Or you can initalize it using the {@link Roo.Element} object:
19216  <pre><code>
19217  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19218      startDrag : function(){
19219          this.constrainTo("parent-id");
19220      }
19221  });
19222  </code></pre>
19223      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19224      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19225      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19226      * an object containing the sides to pad. For example: {right:10, bottom:10}
19227      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19228      */
19229     constrainTo : function(constrainTo, pad, inContent){
19230         if(typeof pad == "number"){
19231             pad = {left: pad, right:pad, top:pad, bottom:pad};
19232         }
19233         pad = pad || this.defaultPadding;
19234         var b = Roo.get(this.getEl()).getBox();
19235         var ce = Roo.get(constrainTo);
19236         var s = ce.getScroll();
19237         var c, cd = ce.dom;
19238         if(cd == document.body){
19239             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19240         }else{
19241             xy = ce.getXY();
19242             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19243         }
19244
19245
19246         var topSpace = b.y - c.y;
19247         var leftSpace = b.x - c.x;
19248
19249         this.resetConstraints();
19250         this.setXConstraint(leftSpace - (pad.left||0), // left
19251                 c.width - leftSpace - b.width - (pad.right||0) //right
19252         );
19253         this.setYConstraint(topSpace - (pad.top||0), //top
19254                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19255         );
19256     },
19257
19258     /**
19259      * Returns a reference to the linked element
19260      * @method getEl
19261      * @return {HTMLElement} the html element
19262      */
19263     getEl: function() {
19264         if (!this._domRef) {
19265             this._domRef = Roo.getDom(this.id);
19266         }
19267
19268         return this._domRef;
19269     },
19270
19271     /**
19272      * Returns a reference to the actual element to drag.  By default this is
19273      * the same as the html element, but it can be assigned to another
19274      * element. An example of this can be found in Roo.dd.DDProxy
19275      * @method getDragEl
19276      * @return {HTMLElement} the html element
19277      */
19278     getDragEl: function() {
19279         return Roo.getDom(this.dragElId);
19280     },
19281
19282     /**
19283      * Sets up the DragDrop object.  Must be called in the constructor of any
19284      * Roo.dd.DragDrop subclass
19285      * @method init
19286      * @param id the id of the linked element
19287      * @param {String} sGroup the group of related items
19288      * @param {object} config configuration attributes
19289      */
19290     init: function(id, sGroup, config) {
19291         this.initTarget(id, sGroup, config);
19292         if (!Roo.isTouch) {
19293             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19294         }
19295         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19296         // Event.on(this.id, "selectstart", Event.preventDefault);
19297     },
19298
19299     /**
19300      * Initializes Targeting functionality only... the object does not
19301      * get a mousedown handler.
19302      * @method initTarget
19303      * @param id the id of the linked element
19304      * @param {String} sGroup the group of related items
19305      * @param {object} config configuration attributes
19306      */
19307     initTarget: function(id, sGroup, config) {
19308
19309         // configuration attributes
19310         this.config = config || {};
19311
19312         // create a local reference to the drag and drop manager
19313         this.DDM = Roo.dd.DDM;
19314         // initialize the groups array
19315         this.groups = {};
19316
19317         // assume that we have an element reference instead of an id if the
19318         // parameter is not a string
19319         if (typeof id !== "string") {
19320             id = Roo.id(id);
19321         }
19322
19323         // set the id
19324         this.id = id;
19325
19326         // add to an interaction group
19327         this.addToGroup((sGroup) ? sGroup : "default");
19328
19329         // We don't want to register this as the handle with the manager
19330         // so we just set the id rather than calling the setter.
19331         this.handleElId = id;
19332
19333         // the linked element is the element that gets dragged by default
19334         this.setDragElId(id);
19335
19336         // by default, clicked anchors will not start drag operations.
19337         this.invalidHandleTypes = { A: "A" };
19338         this.invalidHandleIds = {};
19339         this.invalidHandleClasses = [];
19340
19341         this.applyConfig();
19342
19343         this.handleOnAvailable();
19344     },
19345
19346     /**
19347      * Applies the configuration parameters that were passed into the constructor.
19348      * This is supposed to happen at each level through the inheritance chain.  So
19349      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19350      * DragDrop in order to get all of the parameters that are available in
19351      * each object.
19352      * @method applyConfig
19353      */
19354     applyConfig: function() {
19355
19356         // configurable properties:
19357         //    padding, isTarget, maintainOffset, primaryButtonOnly
19358         this.padding           = this.config.padding || [0, 0, 0, 0];
19359         this.isTarget          = (this.config.isTarget !== false);
19360         this.maintainOffset    = (this.config.maintainOffset);
19361         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19362
19363     },
19364
19365     /**
19366      * Executed when the linked element is available
19367      * @method handleOnAvailable
19368      * @private
19369      */
19370     handleOnAvailable: function() {
19371         this.available = true;
19372         this.resetConstraints();
19373         this.onAvailable();
19374     },
19375
19376      /**
19377      * Configures the padding for the target zone in px.  Effectively expands
19378      * (or reduces) the virtual object size for targeting calculations.
19379      * Supports css-style shorthand; if only one parameter is passed, all sides
19380      * will have that padding, and if only two are passed, the top and bottom
19381      * will have the first param, the left and right the second.
19382      * @method setPadding
19383      * @param {int} iTop    Top pad
19384      * @param {int} iRight  Right pad
19385      * @param {int} iBot    Bot pad
19386      * @param {int} iLeft   Left pad
19387      */
19388     setPadding: function(iTop, iRight, iBot, iLeft) {
19389         // this.padding = [iLeft, iRight, iTop, iBot];
19390         if (!iRight && 0 !== iRight) {
19391             this.padding = [iTop, iTop, iTop, iTop];
19392         } else if (!iBot && 0 !== iBot) {
19393             this.padding = [iTop, iRight, iTop, iRight];
19394         } else {
19395             this.padding = [iTop, iRight, iBot, iLeft];
19396         }
19397     },
19398
19399     /**
19400      * Stores the initial placement of the linked element.
19401      * @method setInitialPosition
19402      * @param {int} diffX   the X offset, default 0
19403      * @param {int} diffY   the Y offset, default 0
19404      */
19405     setInitPosition: function(diffX, diffY) {
19406         var el = this.getEl();
19407
19408         if (!this.DDM.verifyEl(el)) {
19409             return;
19410         }
19411
19412         var dx = diffX || 0;
19413         var dy = diffY || 0;
19414
19415         var p = Dom.getXY( el );
19416
19417         this.initPageX = p[0] - dx;
19418         this.initPageY = p[1] - dy;
19419
19420         this.lastPageX = p[0];
19421         this.lastPageY = p[1];
19422
19423
19424         this.setStartPosition(p);
19425     },
19426
19427     /**
19428      * Sets the start position of the element.  This is set when the obj
19429      * is initialized, the reset when a drag is started.
19430      * @method setStartPosition
19431      * @param pos current position (from previous lookup)
19432      * @private
19433      */
19434     setStartPosition: function(pos) {
19435         var p = pos || Dom.getXY( this.getEl() );
19436         this.deltaSetXY = null;
19437
19438         this.startPageX = p[0];
19439         this.startPageY = p[1];
19440     },
19441
19442     /**
19443      * Add this instance to a group of related drag/drop objects.  All
19444      * instances belong to at least one group, and can belong to as many
19445      * groups as needed.
19446      * @method addToGroup
19447      * @param sGroup {string} the name of the group
19448      */
19449     addToGroup: function(sGroup) {
19450         this.groups[sGroup] = true;
19451         this.DDM.regDragDrop(this, sGroup);
19452     },
19453
19454     /**
19455      * Remove's this instance from the supplied interaction group
19456      * @method removeFromGroup
19457      * @param {string}  sGroup  The group to drop
19458      */
19459     removeFromGroup: function(sGroup) {
19460         if (this.groups[sGroup]) {
19461             delete this.groups[sGroup];
19462         }
19463
19464         this.DDM.removeDDFromGroup(this, sGroup);
19465     },
19466
19467     /**
19468      * Allows you to specify that an element other than the linked element
19469      * will be moved with the cursor during a drag
19470      * @method setDragElId
19471      * @param id {string} the id of the element that will be used to initiate the drag
19472      */
19473     setDragElId: function(id) {
19474         this.dragElId = id;
19475     },
19476
19477     /**
19478      * Allows you to specify a child of the linked element that should be
19479      * used to initiate the drag operation.  An example of this would be if
19480      * you have a content div with text and links.  Clicking anywhere in the
19481      * content area would normally start the drag operation.  Use this method
19482      * to specify that an element inside of the content div is the element
19483      * that starts the drag operation.
19484      * @method setHandleElId
19485      * @param id {string} the id of the element that will be used to
19486      * initiate the drag.
19487      */
19488     setHandleElId: function(id) {
19489         if (typeof id !== "string") {
19490             id = Roo.id(id);
19491         }
19492         this.handleElId = id;
19493         this.DDM.regHandle(this.id, id);
19494     },
19495
19496     /**
19497      * Allows you to set an element outside of the linked element as a drag
19498      * handle
19499      * @method setOuterHandleElId
19500      * @param id the id of the element that will be used to initiate the drag
19501      */
19502     setOuterHandleElId: function(id) {
19503         if (typeof id !== "string") {
19504             id = Roo.id(id);
19505         }
19506         Event.on(id, "mousedown",
19507                 this.handleMouseDown, this);
19508         this.setHandleElId(id);
19509
19510         this.hasOuterHandles = true;
19511     },
19512
19513     /**
19514      * Remove all drag and drop hooks for this element
19515      * @method unreg
19516      */
19517     unreg: function() {
19518         Event.un(this.id, "mousedown",
19519                 this.handleMouseDown);
19520         Event.un(this.id, "touchstart",
19521                 this.handleMouseDown);
19522         this._domRef = null;
19523         this.DDM._remove(this);
19524     },
19525
19526     destroy : function(){
19527         this.unreg();
19528     },
19529
19530     /**
19531      * Returns true if this instance is locked, or the drag drop mgr is locked
19532      * (meaning that all drag/drop is disabled on the page.)
19533      * @method isLocked
19534      * @return {boolean} true if this obj or all drag/drop is locked, else
19535      * false
19536      */
19537     isLocked: function() {
19538         return (this.DDM.isLocked() || this.locked);
19539     },
19540
19541     /**
19542      * Fired when this object is clicked
19543      * @method handleMouseDown
19544      * @param {Event} e
19545      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19546      * @private
19547      */
19548     handleMouseDown: function(e, oDD){
19549      
19550         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19551             //Roo.log('not touch/ button !=0');
19552             return;
19553         }
19554         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19555             return; // double touch..
19556         }
19557         
19558
19559         if (this.isLocked()) {
19560             //Roo.log('locked');
19561             return;
19562         }
19563
19564         this.DDM.refreshCache(this.groups);
19565 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19566         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19567         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19568             //Roo.log('no outer handes or not over target');
19569                 // do nothing.
19570         } else {
19571 //            Roo.log('check validator');
19572             if (this.clickValidator(e)) {
19573 //                Roo.log('validate success');
19574                 // set the initial element position
19575                 this.setStartPosition();
19576
19577
19578                 this.b4MouseDown(e);
19579                 this.onMouseDown(e);
19580
19581                 this.DDM.handleMouseDown(e, this);
19582
19583                 this.DDM.stopEvent(e);
19584             } else {
19585
19586
19587             }
19588         }
19589     },
19590
19591     clickValidator: function(e) {
19592         var target = e.getTarget();
19593         return ( this.isValidHandleChild(target) &&
19594                     (this.id == this.handleElId ||
19595                         this.DDM.handleWasClicked(target, this.id)) );
19596     },
19597
19598     /**
19599      * Allows you to specify a tag name that should not start a drag operation
19600      * when clicked.  This is designed to facilitate embedding links within a
19601      * drag handle that do something other than start the drag.
19602      * @method addInvalidHandleType
19603      * @param {string} tagName the type of element to exclude
19604      */
19605     addInvalidHandleType: function(tagName) {
19606         var type = tagName.toUpperCase();
19607         this.invalidHandleTypes[type] = type;
19608     },
19609
19610     /**
19611      * Lets you to specify an element id for a child of a drag handle
19612      * that should not initiate a drag
19613      * @method addInvalidHandleId
19614      * @param {string} id the element id of the element you wish to ignore
19615      */
19616     addInvalidHandleId: function(id) {
19617         if (typeof id !== "string") {
19618             id = Roo.id(id);
19619         }
19620         this.invalidHandleIds[id] = id;
19621     },
19622
19623     /**
19624      * Lets you specify a css class of elements that will not initiate a drag
19625      * @method addInvalidHandleClass
19626      * @param {string} cssClass the class of the elements you wish to ignore
19627      */
19628     addInvalidHandleClass: function(cssClass) {
19629         this.invalidHandleClasses.push(cssClass);
19630     },
19631
19632     /**
19633      * Unsets an excluded tag name set by addInvalidHandleType
19634      * @method removeInvalidHandleType
19635      * @param {string} tagName the type of element to unexclude
19636      */
19637     removeInvalidHandleType: function(tagName) {
19638         var type = tagName.toUpperCase();
19639         // this.invalidHandleTypes[type] = null;
19640         delete this.invalidHandleTypes[type];
19641     },
19642
19643     /**
19644      * Unsets an invalid handle id
19645      * @method removeInvalidHandleId
19646      * @param {string} id the id of the element to re-enable
19647      */
19648     removeInvalidHandleId: function(id) {
19649         if (typeof id !== "string") {
19650             id = Roo.id(id);
19651         }
19652         delete this.invalidHandleIds[id];
19653     },
19654
19655     /**
19656      * Unsets an invalid css class
19657      * @method removeInvalidHandleClass
19658      * @param {string} cssClass the class of the element(s) you wish to
19659      * re-enable
19660      */
19661     removeInvalidHandleClass: function(cssClass) {
19662         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19663             if (this.invalidHandleClasses[i] == cssClass) {
19664                 delete this.invalidHandleClasses[i];
19665             }
19666         }
19667     },
19668
19669     /**
19670      * Checks the tag exclusion list to see if this click should be ignored
19671      * @method isValidHandleChild
19672      * @param {HTMLElement} node the HTMLElement to evaluate
19673      * @return {boolean} true if this is a valid tag type, false if not
19674      */
19675     isValidHandleChild: function(node) {
19676
19677         var valid = true;
19678         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19679         var nodeName;
19680         try {
19681             nodeName = node.nodeName.toUpperCase();
19682         } catch(e) {
19683             nodeName = node.nodeName;
19684         }
19685         valid = valid && !this.invalidHandleTypes[nodeName];
19686         valid = valid && !this.invalidHandleIds[node.id];
19687
19688         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19689             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19690         }
19691
19692
19693         return valid;
19694
19695     },
19696
19697     /**
19698      * Create the array of horizontal tick marks if an interval was specified
19699      * in setXConstraint().
19700      * @method setXTicks
19701      * @private
19702      */
19703     setXTicks: function(iStartX, iTickSize) {
19704         this.xTicks = [];
19705         this.xTickSize = iTickSize;
19706
19707         var tickMap = {};
19708
19709         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19710             if (!tickMap[i]) {
19711                 this.xTicks[this.xTicks.length] = i;
19712                 tickMap[i] = true;
19713             }
19714         }
19715
19716         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19717             if (!tickMap[i]) {
19718                 this.xTicks[this.xTicks.length] = i;
19719                 tickMap[i] = true;
19720             }
19721         }
19722
19723         this.xTicks.sort(this.DDM.numericSort) ;
19724     },
19725
19726     /**
19727      * Create the array of vertical tick marks if an interval was specified in
19728      * setYConstraint().
19729      * @method setYTicks
19730      * @private
19731      */
19732     setYTicks: function(iStartY, iTickSize) {
19733         this.yTicks = [];
19734         this.yTickSize = iTickSize;
19735
19736         var tickMap = {};
19737
19738         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19739             if (!tickMap[i]) {
19740                 this.yTicks[this.yTicks.length] = i;
19741                 tickMap[i] = true;
19742             }
19743         }
19744
19745         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19746             if (!tickMap[i]) {
19747                 this.yTicks[this.yTicks.length] = i;
19748                 tickMap[i] = true;
19749             }
19750         }
19751
19752         this.yTicks.sort(this.DDM.numericSort) ;
19753     },
19754
19755     /**
19756      * By default, the element can be dragged any place on the screen.  Use
19757      * this method to limit the horizontal travel of the element.  Pass in
19758      * 0,0 for the parameters if you want to lock the drag to the y axis.
19759      * @method setXConstraint
19760      * @param {int} iLeft the number of pixels the element can move to the left
19761      * @param {int} iRight the number of pixels the element can move to the
19762      * right
19763      * @param {int} iTickSize optional parameter for specifying that the
19764      * element
19765      * should move iTickSize pixels at a time.
19766      */
19767     setXConstraint: function(iLeft, iRight, iTickSize) {
19768         this.leftConstraint = iLeft;
19769         this.rightConstraint = iRight;
19770
19771         this.minX = this.initPageX - iLeft;
19772         this.maxX = this.initPageX + iRight;
19773         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19774
19775         this.constrainX = true;
19776     },
19777
19778     /**
19779      * Clears any constraints applied to this instance.  Also clears ticks
19780      * since they can't exist independent of a constraint at this time.
19781      * @method clearConstraints
19782      */
19783     clearConstraints: function() {
19784         this.constrainX = false;
19785         this.constrainY = false;
19786         this.clearTicks();
19787     },
19788
19789     /**
19790      * Clears any tick interval defined for this instance
19791      * @method clearTicks
19792      */
19793     clearTicks: function() {
19794         this.xTicks = null;
19795         this.yTicks = null;
19796         this.xTickSize = 0;
19797         this.yTickSize = 0;
19798     },
19799
19800     /**
19801      * By default, the element can be dragged any place on the screen.  Set
19802      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19803      * parameters if you want to lock the drag to the x axis.
19804      * @method setYConstraint
19805      * @param {int} iUp the number of pixels the element can move up
19806      * @param {int} iDown the number of pixels the element can move down
19807      * @param {int} iTickSize optional parameter for specifying that the
19808      * element should move iTickSize pixels at a time.
19809      */
19810     setYConstraint: function(iUp, iDown, iTickSize) {
19811         this.topConstraint = iUp;
19812         this.bottomConstraint = iDown;
19813
19814         this.minY = this.initPageY - iUp;
19815         this.maxY = this.initPageY + iDown;
19816         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19817
19818         this.constrainY = true;
19819
19820     },
19821
19822     /**
19823      * resetConstraints must be called if you manually reposition a dd element.
19824      * @method resetConstraints
19825      * @param {boolean} maintainOffset
19826      */
19827     resetConstraints: function() {
19828
19829
19830         // Maintain offsets if necessary
19831         if (this.initPageX || this.initPageX === 0) {
19832             // figure out how much this thing has moved
19833             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19834             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19835
19836             this.setInitPosition(dx, dy);
19837
19838         // This is the first time we have detected the element's position
19839         } else {
19840             this.setInitPosition();
19841         }
19842
19843         if (this.constrainX) {
19844             this.setXConstraint( this.leftConstraint,
19845                                  this.rightConstraint,
19846                                  this.xTickSize        );
19847         }
19848
19849         if (this.constrainY) {
19850             this.setYConstraint( this.topConstraint,
19851                                  this.bottomConstraint,
19852                                  this.yTickSize         );
19853         }
19854     },
19855
19856     /**
19857      * Normally the drag element is moved pixel by pixel, but we can specify
19858      * that it move a number of pixels at a time.  This method resolves the
19859      * location when we have it set up like this.
19860      * @method getTick
19861      * @param {int} val where we want to place the object
19862      * @param {int[]} tickArray sorted array of valid points
19863      * @return {int} the closest tick
19864      * @private
19865      */
19866     getTick: function(val, tickArray) {
19867
19868         if (!tickArray) {
19869             // If tick interval is not defined, it is effectively 1 pixel,
19870             // so we return the value passed to us.
19871             return val;
19872         } else if (tickArray[0] >= val) {
19873             // The value is lower than the first tick, so we return the first
19874             // tick.
19875             return tickArray[0];
19876         } else {
19877             for (var i=0, len=tickArray.length; i<len; ++i) {
19878                 var next = i + 1;
19879                 if (tickArray[next] && tickArray[next] >= val) {
19880                     var diff1 = val - tickArray[i];
19881                     var diff2 = tickArray[next] - val;
19882                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19883                 }
19884             }
19885
19886             // The value is larger than the last tick, so we return the last
19887             // tick.
19888             return tickArray[tickArray.length - 1];
19889         }
19890     },
19891
19892     /**
19893      * toString method
19894      * @method toString
19895      * @return {string} string representation of the dd obj
19896      */
19897     toString: function() {
19898         return ("DragDrop " + this.id);
19899     }
19900
19901 });
19902
19903 })();
19904 /*
19905  * Based on:
19906  * Ext JS Library 1.1.1
19907  * Copyright(c) 2006-2007, Ext JS, LLC.
19908  *
19909  * Originally Released Under LGPL - original licence link has changed is not relivant.
19910  *
19911  * Fork - LGPL
19912  * <script type="text/javascript">
19913  */
19914
19915
19916 /**
19917  * The drag and drop utility provides a framework for building drag and drop
19918  * applications.  In addition to enabling drag and drop for specific elements,
19919  * the drag and drop elements are tracked by the manager class, and the
19920  * interactions between the various elements are tracked during the drag and
19921  * the implementing code is notified about these important moments.
19922  */
19923
19924 // Only load the library once.  Rewriting the manager class would orphan
19925 // existing drag and drop instances.
19926 if (!Roo.dd.DragDropMgr) {
19927
19928 /**
19929  * @class Roo.dd.DragDropMgr
19930  * DragDropMgr is a singleton that tracks the element interaction for
19931  * all DragDrop items in the window.  Generally, you will not call
19932  * this class directly, but it does have helper methods that could
19933  * be useful in your DragDrop implementations.
19934  * @singleton
19935  */
19936 Roo.dd.DragDropMgr = function() {
19937
19938     var Event = Roo.EventManager;
19939
19940     return {
19941
19942         /**
19943          * Two dimensional Array of registered DragDrop objects.  The first
19944          * dimension is the DragDrop item group, the second the DragDrop
19945          * object.
19946          * @property ids
19947          * @type {string: string}
19948          * @private
19949          * @static
19950          */
19951         ids: {},
19952
19953         /**
19954          * Array of element ids defined as drag handles.  Used to determine
19955          * if the element that generated the mousedown event is actually the
19956          * handle and not the html element itself.
19957          * @property handleIds
19958          * @type {string: string}
19959          * @private
19960          * @static
19961          */
19962         handleIds: {},
19963
19964         /**
19965          * the DragDrop object that is currently being dragged
19966          * @property dragCurrent
19967          * @type DragDrop
19968          * @private
19969          * @static
19970          **/
19971         dragCurrent: null,
19972
19973         /**
19974          * the DragDrop object(s) that are being hovered over
19975          * @property dragOvers
19976          * @type Array
19977          * @private
19978          * @static
19979          */
19980         dragOvers: {},
19981
19982         /**
19983          * the X distance between the cursor and the object being dragged
19984          * @property deltaX
19985          * @type int
19986          * @private
19987          * @static
19988          */
19989         deltaX: 0,
19990
19991         /**
19992          * the Y distance between the cursor and the object being dragged
19993          * @property deltaY
19994          * @type int
19995          * @private
19996          * @static
19997          */
19998         deltaY: 0,
19999
20000         /**
20001          * Flag to determine if we should prevent the default behavior of the
20002          * events we define. By default this is true, but this can be set to
20003          * false if you need the default behavior (not recommended)
20004          * @property preventDefault
20005          * @type boolean
20006          * @static
20007          */
20008         preventDefault: true,
20009
20010         /**
20011          * Flag to determine if we should stop the propagation of the events
20012          * we generate. This is true by default but you may want to set it to
20013          * false if the html element contains other features that require the
20014          * mouse click.
20015          * @property stopPropagation
20016          * @type boolean
20017          * @static
20018          */
20019         stopPropagation: true,
20020
20021         /**
20022          * Internal flag that is set to true when drag and drop has been
20023          * intialized
20024          * @property initialized
20025          * @private
20026          * @static
20027          */
20028         initalized: false,
20029
20030         /**
20031          * All drag and drop can be disabled.
20032          * @property locked
20033          * @private
20034          * @static
20035          */
20036         locked: false,
20037
20038         /**
20039          * Called the first time an element is registered.
20040          * @method init
20041          * @private
20042          * @static
20043          */
20044         init: function() {
20045             this.initialized = true;
20046         },
20047
20048         /**
20049          * In point mode, drag and drop interaction is defined by the
20050          * location of the cursor during the drag/drop
20051          * @property POINT
20052          * @type int
20053          * @static
20054          */
20055         POINT: 0,
20056
20057         /**
20058          * In intersect mode, drag and drop interactio nis defined by the
20059          * overlap of two or more drag and drop objects.
20060          * @property INTERSECT
20061          * @type int
20062          * @static
20063          */
20064         INTERSECT: 1,
20065
20066         /**
20067          * The current drag and drop mode.  Default: POINT
20068          * @property mode
20069          * @type int
20070          * @static
20071          */
20072         mode: 0,
20073
20074         /**
20075          * Runs method on all drag and drop objects
20076          * @method _execOnAll
20077          * @private
20078          * @static
20079          */
20080         _execOnAll: function(sMethod, args) {
20081             for (var i in this.ids) {
20082                 for (var j in this.ids[i]) {
20083                     var oDD = this.ids[i][j];
20084                     if (! this.isTypeOfDD(oDD)) {
20085                         continue;
20086                     }
20087                     oDD[sMethod].apply(oDD, args);
20088                 }
20089             }
20090         },
20091
20092         /**
20093          * Drag and drop initialization.  Sets up the global event handlers
20094          * @method _onLoad
20095          * @private
20096          * @static
20097          */
20098         _onLoad: function() {
20099
20100             this.init();
20101
20102             if (!Roo.isTouch) {
20103                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20104                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20105             }
20106             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20107             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20108             
20109             Event.on(window,   "unload",    this._onUnload, this, true);
20110             Event.on(window,   "resize",    this._onResize, this, true);
20111             // Event.on(window,   "mouseout",    this._test);
20112
20113         },
20114
20115         /**
20116          * Reset constraints on all drag and drop objs
20117          * @method _onResize
20118          * @private
20119          * @static
20120          */
20121         _onResize: function(e) {
20122             this._execOnAll("resetConstraints", []);
20123         },
20124
20125         /**
20126          * Lock all drag and drop functionality
20127          * @method lock
20128          * @static
20129          */
20130         lock: function() { this.locked = true; },
20131
20132         /**
20133          * Unlock all drag and drop functionality
20134          * @method unlock
20135          * @static
20136          */
20137         unlock: function() { this.locked = false; },
20138
20139         /**
20140          * Is drag and drop locked?
20141          * @method isLocked
20142          * @return {boolean} True if drag and drop is locked, false otherwise.
20143          * @static
20144          */
20145         isLocked: function() { return this.locked; },
20146
20147         /**
20148          * Location cache that is set for all drag drop objects when a drag is
20149          * initiated, cleared when the drag is finished.
20150          * @property locationCache
20151          * @private
20152          * @static
20153          */
20154         locationCache: {},
20155
20156         /**
20157          * Set useCache to false if you want to force object the lookup of each
20158          * drag and drop linked element constantly during a drag.
20159          * @property useCache
20160          * @type boolean
20161          * @static
20162          */
20163         useCache: true,
20164
20165         /**
20166          * The number of pixels that the mouse needs to move after the
20167          * mousedown before the drag is initiated.  Default=3;
20168          * @property clickPixelThresh
20169          * @type int
20170          * @static
20171          */
20172         clickPixelThresh: 3,
20173
20174         /**
20175          * The number of milliseconds after the mousedown event to initiate the
20176          * drag if we don't get a mouseup event. Default=1000
20177          * @property clickTimeThresh
20178          * @type int
20179          * @static
20180          */
20181         clickTimeThresh: 350,
20182
20183         /**
20184          * Flag that indicates that either the drag pixel threshold or the
20185          * mousdown time threshold has been met
20186          * @property dragThreshMet
20187          * @type boolean
20188          * @private
20189          * @static
20190          */
20191         dragThreshMet: false,
20192
20193         /**
20194          * Timeout used for the click time threshold
20195          * @property clickTimeout
20196          * @type Object
20197          * @private
20198          * @static
20199          */
20200         clickTimeout: null,
20201
20202         /**
20203          * The X position of the mousedown event stored for later use when a
20204          * drag threshold is met.
20205          * @property startX
20206          * @type int
20207          * @private
20208          * @static
20209          */
20210         startX: 0,
20211
20212         /**
20213          * The Y position of the mousedown event stored for later use when a
20214          * drag threshold is met.
20215          * @property startY
20216          * @type int
20217          * @private
20218          * @static
20219          */
20220         startY: 0,
20221
20222         /**
20223          * Each DragDrop instance must be registered with the DragDropMgr.
20224          * This is executed in DragDrop.init()
20225          * @method regDragDrop
20226          * @param {DragDrop} oDD the DragDrop object to register
20227          * @param {String} sGroup the name of the group this element belongs to
20228          * @static
20229          */
20230         regDragDrop: function(oDD, sGroup) {
20231             if (!this.initialized) { this.init(); }
20232
20233             if (!this.ids[sGroup]) {
20234                 this.ids[sGroup] = {};
20235             }
20236             this.ids[sGroup][oDD.id] = oDD;
20237         },
20238
20239         /**
20240          * Removes the supplied dd instance from the supplied group. Executed
20241          * by DragDrop.removeFromGroup, so don't call this function directly.
20242          * @method removeDDFromGroup
20243          * @private
20244          * @static
20245          */
20246         removeDDFromGroup: function(oDD, sGroup) {
20247             if (!this.ids[sGroup]) {
20248                 this.ids[sGroup] = {};
20249             }
20250
20251             var obj = this.ids[sGroup];
20252             if (obj && obj[oDD.id]) {
20253                 delete obj[oDD.id];
20254             }
20255         },
20256
20257         /**
20258          * Unregisters a drag and drop item.  This is executed in
20259          * DragDrop.unreg, use that method instead of calling this directly.
20260          * @method _remove
20261          * @private
20262          * @static
20263          */
20264         _remove: function(oDD) {
20265             for (var g in oDD.groups) {
20266                 if (g && this.ids[g][oDD.id]) {
20267                     delete this.ids[g][oDD.id];
20268                 }
20269             }
20270             delete this.handleIds[oDD.id];
20271         },
20272
20273         /**
20274          * Each DragDrop handle element must be registered.  This is done
20275          * automatically when executing DragDrop.setHandleElId()
20276          * @method regHandle
20277          * @param {String} sDDId the DragDrop id this element is a handle for
20278          * @param {String} sHandleId the id of the element that is the drag
20279          * handle
20280          * @static
20281          */
20282         regHandle: function(sDDId, sHandleId) {
20283             if (!this.handleIds[sDDId]) {
20284                 this.handleIds[sDDId] = {};
20285             }
20286             this.handleIds[sDDId][sHandleId] = sHandleId;
20287         },
20288
20289         /**
20290          * Utility function to determine if a given element has been
20291          * registered as a drag drop item.
20292          * @method isDragDrop
20293          * @param {String} id the element id to check
20294          * @return {boolean} true if this element is a DragDrop item,
20295          * false otherwise
20296          * @static
20297          */
20298         isDragDrop: function(id) {
20299             return ( this.getDDById(id) ) ? true : false;
20300         },
20301
20302         /**
20303          * Returns the drag and drop instances that are in all groups the
20304          * passed in instance belongs to.
20305          * @method getRelated
20306          * @param {DragDrop} p_oDD the obj to get related data for
20307          * @param {boolean} bTargetsOnly if true, only return targetable objs
20308          * @return {DragDrop[]} the related instances
20309          * @static
20310          */
20311         getRelated: function(p_oDD, bTargetsOnly) {
20312             var oDDs = [];
20313             for (var i in p_oDD.groups) {
20314                 for (j in this.ids[i]) {
20315                     var dd = this.ids[i][j];
20316                     if (! this.isTypeOfDD(dd)) {
20317                         continue;
20318                     }
20319                     if (!bTargetsOnly || dd.isTarget) {
20320                         oDDs[oDDs.length] = dd;
20321                     }
20322                 }
20323             }
20324
20325             return oDDs;
20326         },
20327
20328         /**
20329          * Returns true if the specified dd target is a legal target for
20330          * the specifice drag obj
20331          * @method isLegalTarget
20332          * @param {DragDrop} the drag obj
20333          * @param {DragDrop} the target
20334          * @return {boolean} true if the target is a legal target for the
20335          * dd obj
20336          * @static
20337          */
20338         isLegalTarget: function (oDD, oTargetDD) {
20339             var targets = this.getRelated(oDD, true);
20340             for (var i=0, len=targets.length;i<len;++i) {
20341                 if (targets[i].id == oTargetDD.id) {
20342                     return true;
20343                 }
20344             }
20345
20346             return false;
20347         },
20348
20349         /**
20350          * My goal is to be able to transparently determine if an object is
20351          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20352          * returns "object", oDD.constructor.toString() always returns
20353          * "DragDrop" and not the name of the subclass.  So for now it just
20354          * evaluates a well-known variable in DragDrop.
20355          * @method isTypeOfDD
20356          * @param {Object} the object to evaluate
20357          * @return {boolean} true if typeof oDD = DragDrop
20358          * @static
20359          */
20360         isTypeOfDD: function (oDD) {
20361             return (oDD && oDD.__ygDragDrop);
20362         },
20363
20364         /**
20365          * Utility function to determine if a given element has been
20366          * registered as a drag drop handle for the given Drag Drop object.
20367          * @method isHandle
20368          * @param {String} id the element id to check
20369          * @return {boolean} true if this element is a DragDrop handle, false
20370          * otherwise
20371          * @static
20372          */
20373         isHandle: function(sDDId, sHandleId) {
20374             return ( this.handleIds[sDDId] &&
20375                             this.handleIds[sDDId][sHandleId] );
20376         },
20377
20378         /**
20379          * Returns the DragDrop instance for a given id
20380          * @method getDDById
20381          * @param {String} id the id of the DragDrop object
20382          * @return {DragDrop} the drag drop object, null if it is not found
20383          * @static
20384          */
20385         getDDById: function(id) {
20386             for (var i in this.ids) {
20387                 if (this.ids[i][id]) {
20388                     return this.ids[i][id];
20389                 }
20390             }
20391             return null;
20392         },
20393
20394         /**
20395          * Fired after a registered DragDrop object gets the mousedown event.
20396          * Sets up the events required to track the object being dragged
20397          * @method handleMouseDown
20398          * @param {Event} e the event
20399          * @param oDD the DragDrop object being dragged
20400          * @private
20401          * @static
20402          */
20403         handleMouseDown: function(e, oDD) {
20404             if(Roo.QuickTips){
20405                 Roo.QuickTips.disable();
20406             }
20407             this.currentTarget = e.getTarget();
20408
20409             this.dragCurrent = oDD;
20410
20411             var el = oDD.getEl();
20412
20413             // track start position
20414             this.startX = e.getPageX();
20415             this.startY = e.getPageY();
20416
20417             this.deltaX = this.startX - el.offsetLeft;
20418             this.deltaY = this.startY - el.offsetTop;
20419
20420             this.dragThreshMet = false;
20421
20422             this.clickTimeout = setTimeout(
20423                     function() {
20424                         var DDM = Roo.dd.DDM;
20425                         DDM.startDrag(DDM.startX, DDM.startY);
20426                     },
20427                     this.clickTimeThresh );
20428         },
20429
20430         /**
20431          * Fired when either the drag pixel threshol or the mousedown hold
20432          * time threshold has been met.
20433          * @method startDrag
20434          * @param x {int} the X position of the original mousedown
20435          * @param y {int} the Y position of the original mousedown
20436          * @static
20437          */
20438         startDrag: function(x, y) {
20439             clearTimeout(this.clickTimeout);
20440             if (this.dragCurrent) {
20441                 this.dragCurrent.b4StartDrag(x, y);
20442                 this.dragCurrent.startDrag(x, y);
20443             }
20444             this.dragThreshMet = true;
20445         },
20446
20447         /**
20448          * Internal function to handle the mouseup event.  Will be invoked
20449          * from the context of the document.
20450          * @method handleMouseUp
20451          * @param {Event} e the event
20452          * @private
20453          * @static
20454          */
20455         handleMouseUp: function(e) {
20456
20457             if(Roo.QuickTips){
20458                 Roo.QuickTips.enable();
20459             }
20460             if (! this.dragCurrent) {
20461                 return;
20462             }
20463
20464             clearTimeout(this.clickTimeout);
20465
20466             if (this.dragThreshMet) {
20467                 this.fireEvents(e, true);
20468             } else {
20469             }
20470
20471             this.stopDrag(e);
20472
20473             this.stopEvent(e);
20474         },
20475
20476         /**
20477          * Utility to stop event propagation and event default, if these
20478          * features are turned on.
20479          * @method stopEvent
20480          * @param {Event} e the event as returned by this.getEvent()
20481          * @static
20482          */
20483         stopEvent: function(e){
20484             if(this.stopPropagation) {
20485                 e.stopPropagation();
20486             }
20487
20488             if (this.preventDefault) {
20489                 e.preventDefault();
20490             }
20491         },
20492
20493         /**
20494          * Internal function to clean up event handlers after the drag
20495          * operation is complete
20496          * @method stopDrag
20497          * @param {Event} e the event
20498          * @private
20499          * @static
20500          */
20501         stopDrag: function(e) {
20502             // Fire the drag end event for the item that was dragged
20503             if (this.dragCurrent) {
20504                 if (this.dragThreshMet) {
20505                     this.dragCurrent.b4EndDrag(e);
20506                     this.dragCurrent.endDrag(e);
20507                 }
20508
20509                 this.dragCurrent.onMouseUp(e);
20510             }
20511
20512             this.dragCurrent = null;
20513             this.dragOvers = {};
20514         },
20515
20516         /**
20517          * Internal function to handle the mousemove event.  Will be invoked
20518          * from the context of the html element.
20519          *
20520          * @TODO figure out what we can do about mouse events lost when the
20521          * user drags objects beyond the window boundary.  Currently we can
20522          * detect this in internet explorer by verifying that the mouse is
20523          * down during the mousemove event.  Firefox doesn't give us the
20524          * button state on the mousemove event.
20525          * @method handleMouseMove
20526          * @param {Event} e the event
20527          * @private
20528          * @static
20529          */
20530         handleMouseMove: function(e) {
20531             if (! this.dragCurrent) {
20532                 return true;
20533             }
20534
20535             // var button = e.which || e.button;
20536
20537             // check for IE mouseup outside of page boundary
20538             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20539                 this.stopEvent(e);
20540                 return this.handleMouseUp(e);
20541             }
20542
20543             if (!this.dragThreshMet) {
20544                 var diffX = Math.abs(this.startX - e.getPageX());
20545                 var diffY = Math.abs(this.startY - e.getPageY());
20546                 if (diffX > this.clickPixelThresh ||
20547                             diffY > this.clickPixelThresh) {
20548                     this.startDrag(this.startX, this.startY);
20549                 }
20550             }
20551
20552             if (this.dragThreshMet) {
20553                 this.dragCurrent.b4Drag(e);
20554                 this.dragCurrent.onDrag(e);
20555                 if(!this.dragCurrent.moveOnly){
20556                     this.fireEvents(e, false);
20557                 }
20558             }
20559
20560             this.stopEvent(e);
20561
20562             return true;
20563         },
20564
20565         /**
20566          * Iterates over all of the DragDrop elements to find ones we are
20567          * hovering over or dropping on
20568          * @method fireEvents
20569          * @param {Event} e the event
20570          * @param {boolean} isDrop is this a drop op or a mouseover op?
20571          * @private
20572          * @static
20573          */
20574         fireEvents: function(e, isDrop) {
20575             var dc = this.dragCurrent;
20576
20577             // If the user did the mouse up outside of the window, we could
20578             // get here even though we have ended the drag.
20579             if (!dc || dc.isLocked()) {
20580                 return;
20581             }
20582
20583             var pt = e.getPoint();
20584
20585             // cache the previous dragOver array
20586             var oldOvers = [];
20587
20588             var outEvts   = [];
20589             var overEvts  = [];
20590             var dropEvts  = [];
20591             var enterEvts = [];
20592
20593             // Check to see if the object(s) we were hovering over is no longer
20594             // being hovered over so we can fire the onDragOut event
20595             for (var i in this.dragOvers) {
20596
20597                 var ddo = this.dragOvers[i];
20598
20599                 if (! this.isTypeOfDD(ddo)) {
20600                     continue;
20601                 }
20602
20603                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20604                     outEvts.push( ddo );
20605                 }
20606
20607                 oldOvers[i] = true;
20608                 delete this.dragOvers[i];
20609             }
20610
20611             for (var sGroup in dc.groups) {
20612
20613                 if ("string" != typeof sGroup) {
20614                     continue;
20615                 }
20616
20617                 for (i in this.ids[sGroup]) {
20618                     var oDD = this.ids[sGroup][i];
20619                     if (! this.isTypeOfDD(oDD)) {
20620                         continue;
20621                     }
20622
20623                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20624                         if (this.isOverTarget(pt, oDD, this.mode)) {
20625                             // look for drop interactions
20626                             if (isDrop) {
20627                                 dropEvts.push( oDD );
20628                             // look for drag enter and drag over interactions
20629                             } else {
20630
20631                                 // initial drag over: dragEnter fires
20632                                 if (!oldOvers[oDD.id]) {
20633                                     enterEvts.push( oDD );
20634                                 // subsequent drag overs: dragOver fires
20635                                 } else {
20636                                     overEvts.push( oDD );
20637                                 }
20638
20639                                 this.dragOvers[oDD.id] = oDD;
20640                             }
20641                         }
20642                     }
20643                 }
20644             }
20645
20646             if (this.mode) {
20647                 if (outEvts.length) {
20648                     dc.b4DragOut(e, outEvts);
20649                     dc.onDragOut(e, outEvts);
20650                 }
20651
20652                 if (enterEvts.length) {
20653                     dc.onDragEnter(e, enterEvts);
20654                 }
20655
20656                 if (overEvts.length) {
20657                     dc.b4DragOver(e, overEvts);
20658                     dc.onDragOver(e, overEvts);
20659                 }
20660
20661                 if (dropEvts.length) {
20662                     dc.b4DragDrop(e, dropEvts);
20663                     dc.onDragDrop(e, dropEvts);
20664                 }
20665
20666             } else {
20667                 // fire dragout events
20668                 var len = 0;
20669                 for (i=0, len=outEvts.length; i<len; ++i) {
20670                     dc.b4DragOut(e, outEvts[i].id);
20671                     dc.onDragOut(e, outEvts[i].id);
20672                 }
20673
20674                 // fire enter events
20675                 for (i=0,len=enterEvts.length; i<len; ++i) {
20676                     // dc.b4DragEnter(e, oDD.id);
20677                     dc.onDragEnter(e, enterEvts[i].id);
20678                 }
20679
20680                 // fire over events
20681                 for (i=0,len=overEvts.length; i<len; ++i) {
20682                     dc.b4DragOver(e, overEvts[i].id);
20683                     dc.onDragOver(e, overEvts[i].id);
20684                 }
20685
20686                 // fire drop events
20687                 for (i=0, len=dropEvts.length; i<len; ++i) {
20688                     dc.b4DragDrop(e, dropEvts[i].id);
20689                     dc.onDragDrop(e, dropEvts[i].id);
20690                 }
20691
20692             }
20693
20694             // notify about a drop that did not find a target
20695             if (isDrop && !dropEvts.length) {
20696                 dc.onInvalidDrop(e);
20697             }
20698
20699         },
20700
20701         /**
20702          * Helper function for getting the best match from the list of drag
20703          * and drop objects returned by the drag and drop events when we are
20704          * in INTERSECT mode.  It returns either the first object that the
20705          * cursor is over, or the object that has the greatest overlap with
20706          * the dragged element.
20707          * @method getBestMatch
20708          * @param  {DragDrop[]} dds The array of drag and drop objects
20709          * targeted
20710          * @return {DragDrop}       The best single match
20711          * @static
20712          */
20713         getBestMatch: function(dds) {
20714             var winner = null;
20715             // Return null if the input is not what we expect
20716             //if (!dds || !dds.length || dds.length == 0) {
20717                // winner = null;
20718             // If there is only one item, it wins
20719             //} else if (dds.length == 1) {
20720
20721             var len = dds.length;
20722
20723             if (len == 1) {
20724                 winner = dds[0];
20725             } else {
20726                 // Loop through the targeted items
20727                 for (var i=0; i<len; ++i) {
20728                     var dd = dds[i];
20729                     // If the cursor is over the object, it wins.  If the
20730                     // cursor is over multiple matches, the first one we come
20731                     // to wins.
20732                     if (dd.cursorIsOver) {
20733                         winner = dd;
20734                         break;
20735                     // Otherwise the object with the most overlap wins
20736                     } else {
20737                         if (!winner ||
20738                             winner.overlap.getArea() < dd.overlap.getArea()) {
20739                             winner = dd;
20740                         }
20741                     }
20742                 }
20743             }
20744
20745             return winner;
20746         },
20747
20748         /**
20749          * Refreshes the cache of the top-left and bottom-right points of the
20750          * drag and drop objects in the specified group(s).  This is in the
20751          * format that is stored in the drag and drop instance, so typical
20752          * usage is:
20753          * <code>
20754          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20755          * </code>
20756          * Alternatively:
20757          * <code>
20758          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20759          * </code>
20760          * @TODO this really should be an indexed array.  Alternatively this
20761          * method could accept both.
20762          * @method refreshCache
20763          * @param {Object} groups an associative array of groups to refresh
20764          * @static
20765          */
20766         refreshCache: function(groups) {
20767             for (var sGroup in groups) {
20768                 if ("string" != typeof sGroup) {
20769                     continue;
20770                 }
20771                 for (var i in this.ids[sGroup]) {
20772                     var oDD = this.ids[sGroup][i];
20773
20774                     if (this.isTypeOfDD(oDD)) {
20775                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20776                         var loc = this.getLocation(oDD);
20777                         if (loc) {
20778                             this.locationCache[oDD.id] = loc;
20779                         } else {
20780                             delete this.locationCache[oDD.id];
20781                             // this will unregister the drag and drop object if
20782                             // the element is not in a usable state
20783                             // oDD.unreg();
20784                         }
20785                     }
20786                 }
20787             }
20788         },
20789
20790         /**
20791          * This checks to make sure an element exists and is in the DOM.  The
20792          * main purpose is to handle cases where innerHTML is used to remove
20793          * drag and drop objects from the DOM.  IE provides an 'unspecified
20794          * error' when trying to access the offsetParent of such an element
20795          * @method verifyEl
20796          * @param {HTMLElement} el the element to check
20797          * @return {boolean} true if the element looks usable
20798          * @static
20799          */
20800         verifyEl: function(el) {
20801             if (el) {
20802                 var parent;
20803                 if(Roo.isIE){
20804                     try{
20805                         parent = el.offsetParent;
20806                     }catch(e){}
20807                 }else{
20808                     parent = el.offsetParent;
20809                 }
20810                 if (parent) {
20811                     return true;
20812                 }
20813             }
20814
20815             return false;
20816         },
20817
20818         /**
20819          * Returns a Region object containing the drag and drop element's position
20820          * and size, including the padding configured for it
20821          * @method getLocation
20822          * @param {DragDrop} oDD the drag and drop object to get the
20823          *                       location for
20824          * @return {Roo.lib.Region} a Region object representing the total area
20825          *                             the element occupies, including any padding
20826          *                             the instance is configured for.
20827          * @static
20828          */
20829         getLocation: function(oDD) {
20830             if (! this.isTypeOfDD(oDD)) {
20831                 return null;
20832             }
20833
20834             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20835
20836             try {
20837                 pos= Roo.lib.Dom.getXY(el);
20838             } catch (e) { }
20839
20840             if (!pos) {
20841                 return null;
20842             }
20843
20844             x1 = pos[0];
20845             x2 = x1 + el.offsetWidth;
20846             y1 = pos[1];
20847             y2 = y1 + el.offsetHeight;
20848
20849             t = y1 - oDD.padding[0];
20850             r = x2 + oDD.padding[1];
20851             b = y2 + oDD.padding[2];
20852             l = x1 - oDD.padding[3];
20853
20854             return new Roo.lib.Region( t, r, b, l );
20855         },
20856
20857         /**
20858          * Checks the cursor location to see if it over the target
20859          * @method isOverTarget
20860          * @param {Roo.lib.Point} pt The point to evaluate
20861          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20862          * @return {boolean} true if the mouse is over the target
20863          * @private
20864          * @static
20865          */
20866         isOverTarget: function(pt, oTarget, intersect) {
20867             // use cache if available
20868             var loc = this.locationCache[oTarget.id];
20869             if (!loc || !this.useCache) {
20870                 loc = this.getLocation(oTarget);
20871                 this.locationCache[oTarget.id] = loc;
20872
20873             }
20874
20875             if (!loc) {
20876                 return false;
20877             }
20878
20879             oTarget.cursorIsOver = loc.contains( pt );
20880
20881             // DragDrop is using this as a sanity check for the initial mousedown
20882             // in this case we are done.  In POINT mode, if the drag obj has no
20883             // contraints, we are also done. Otherwise we need to evaluate the
20884             // location of the target as related to the actual location of the
20885             // dragged element.
20886             var dc = this.dragCurrent;
20887             if (!dc || !dc.getTargetCoord ||
20888                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20889                 return oTarget.cursorIsOver;
20890             }
20891
20892             oTarget.overlap = null;
20893
20894             // Get the current location of the drag element, this is the
20895             // location of the mouse event less the delta that represents
20896             // where the original mousedown happened on the element.  We
20897             // need to consider constraints and ticks as well.
20898             var pos = dc.getTargetCoord(pt.x, pt.y);
20899
20900             var el = dc.getDragEl();
20901             var curRegion = new Roo.lib.Region( pos.y,
20902                                                    pos.x + el.offsetWidth,
20903                                                    pos.y + el.offsetHeight,
20904                                                    pos.x );
20905
20906             var overlap = curRegion.intersect(loc);
20907
20908             if (overlap) {
20909                 oTarget.overlap = overlap;
20910                 return (intersect) ? true : oTarget.cursorIsOver;
20911             } else {
20912                 return false;
20913             }
20914         },
20915
20916         /**
20917          * unload event handler
20918          * @method _onUnload
20919          * @private
20920          * @static
20921          */
20922         _onUnload: function(e, me) {
20923             Roo.dd.DragDropMgr.unregAll();
20924         },
20925
20926         /**
20927          * Cleans up the drag and drop events and objects.
20928          * @method unregAll
20929          * @private
20930          * @static
20931          */
20932         unregAll: function() {
20933
20934             if (this.dragCurrent) {
20935                 this.stopDrag();
20936                 this.dragCurrent = null;
20937             }
20938
20939             this._execOnAll("unreg", []);
20940
20941             for (i in this.elementCache) {
20942                 delete this.elementCache[i];
20943             }
20944
20945             this.elementCache = {};
20946             this.ids = {};
20947         },
20948
20949         /**
20950          * A cache of DOM elements
20951          * @property elementCache
20952          * @private
20953          * @static
20954          */
20955         elementCache: {},
20956
20957         /**
20958          * Get the wrapper for the DOM element specified
20959          * @method getElWrapper
20960          * @param {String} id the id of the element to get
20961          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
20962          * @private
20963          * @deprecated This wrapper isn't that useful
20964          * @static
20965          */
20966         getElWrapper: function(id) {
20967             var oWrapper = this.elementCache[id];
20968             if (!oWrapper || !oWrapper.el) {
20969                 oWrapper = this.elementCache[id] =
20970                     new this.ElementWrapper(Roo.getDom(id));
20971             }
20972             return oWrapper;
20973         },
20974
20975         /**
20976          * Returns the actual DOM element
20977          * @method getElement
20978          * @param {String} id the id of the elment to get
20979          * @return {Object} The element
20980          * @deprecated use Roo.getDom instead
20981          * @static
20982          */
20983         getElement: function(id) {
20984             return Roo.getDom(id);
20985         },
20986
20987         /**
20988          * Returns the style property for the DOM element (i.e.,
20989          * document.getElById(id).style)
20990          * @method getCss
20991          * @param {String} id the id of the elment to get
20992          * @return {Object} The style property of the element
20993          * @deprecated use Roo.getDom instead
20994          * @static
20995          */
20996         getCss: function(id) {
20997             var el = Roo.getDom(id);
20998             return (el) ? el.style : null;
20999         },
21000
21001         /**
21002          * Inner class for cached elements
21003          * @class DragDropMgr.ElementWrapper
21004          * @for DragDropMgr
21005          * @private
21006          * @deprecated
21007          */
21008         ElementWrapper: function(el) {
21009                 /**
21010                  * The element
21011                  * @property el
21012                  */
21013                 this.el = el || null;
21014                 /**
21015                  * The element id
21016                  * @property id
21017                  */
21018                 this.id = this.el && el.id;
21019                 /**
21020                  * A reference to the style property
21021                  * @property css
21022                  */
21023                 this.css = this.el && el.style;
21024             },
21025
21026         /**
21027          * Returns the X position of an html element
21028          * @method getPosX
21029          * @param el the element for which to get the position
21030          * @return {int} the X coordinate
21031          * @for DragDropMgr
21032          * @deprecated use Roo.lib.Dom.getX instead
21033          * @static
21034          */
21035         getPosX: function(el) {
21036             return Roo.lib.Dom.getX(el);
21037         },
21038
21039         /**
21040          * Returns the Y position of an html element
21041          * @method getPosY
21042          * @param el the element for which to get the position
21043          * @return {int} the Y coordinate
21044          * @deprecated use Roo.lib.Dom.getY instead
21045          * @static
21046          */
21047         getPosY: function(el) {
21048             return Roo.lib.Dom.getY(el);
21049         },
21050
21051         /**
21052          * Swap two nodes.  In IE, we use the native method, for others we
21053          * emulate the IE behavior
21054          * @method swapNode
21055          * @param n1 the first node to swap
21056          * @param n2 the other node to swap
21057          * @static
21058          */
21059         swapNode: function(n1, n2) {
21060             if (n1.swapNode) {
21061                 n1.swapNode(n2);
21062             } else {
21063                 var p = n2.parentNode;
21064                 var s = n2.nextSibling;
21065
21066                 if (s == n1) {
21067                     p.insertBefore(n1, n2);
21068                 } else if (n2 == n1.nextSibling) {
21069                     p.insertBefore(n2, n1);
21070                 } else {
21071                     n1.parentNode.replaceChild(n2, n1);
21072                     p.insertBefore(n1, s);
21073                 }
21074             }
21075         },
21076
21077         /**
21078          * Returns the current scroll position
21079          * @method getScroll
21080          * @private
21081          * @static
21082          */
21083         getScroll: function () {
21084             var t, l, dde=document.documentElement, db=document.body;
21085             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21086                 t = dde.scrollTop;
21087                 l = dde.scrollLeft;
21088             } else if (db) {
21089                 t = db.scrollTop;
21090                 l = db.scrollLeft;
21091             } else {
21092
21093             }
21094             return { top: t, left: l };
21095         },
21096
21097         /**
21098          * Returns the specified element style property
21099          * @method getStyle
21100          * @param {HTMLElement} el          the element
21101          * @param {string}      styleProp   the style property
21102          * @return {string} The value of the style property
21103          * @deprecated use Roo.lib.Dom.getStyle
21104          * @static
21105          */
21106         getStyle: function(el, styleProp) {
21107             return Roo.fly(el).getStyle(styleProp);
21108         },
21109
21110         /**
21111          * Gets the scrollTop
21112          * @method getScrollTop
21113          * @return {int} the document's scrollTop
21114          * @static
21115          */
21116         getScrollTop: function () { return this.getScroll().top; },
21117
21118         /**
21119          * Gets the scrollLeft
21120          * @method getScrollLeft
21121          * @return {int} the document's scrollTop
21122          * @static
21123          */
21124         getScrollLeft: function () { return this.getScroll().left; },
21125
21126         /**
21127          * Sets the x/y position of an element to the location of the
21128          * target element.
21129          * @method moveToEl
21130          * @param {HTMLElement} moveEl      The element to move
21131          * @param {HTMLElement} targetEl    The position reference element
21132          * @static
21133          */
21134         moveToEl: function (moveEl, targetEl) {
21135             var aCoord = Roo.lib.Dom.getXY(targetEl);
21136             Roo.lib.Dom.setXY(moveEl, aCoord);
21137         },
21138
21139         /**
21140          * Numeric array sort function
21141          * @method numericSort
21142          * @static
21143          */
21144         numericSort: function(a, b) { return (a - b); },
21145
21146         /**
21147          * Internal counter
21148          * @property _timeoutCount
21149          * @private
21150          * @static
21151          */
21152         _timeoutCount: 0,
21153
21154         /**
21155          * Trying to make the load order less important.  Without this we get
21156          * an error if this file is loaded before the Event Utility.
21157          * @method _addListeners
21158          * @private
21159          * @static
21160          */
21161         _addListeners: function() {
21162             var DDM = Roo.dd.DDM;
21163             if ( Roo.lib.Event && document ) {
21164                 DDM._onLoad();
21165             } else {
21166                 if (DDM._timeoutCount > 2000) {
21167                 } else {
21168                     setTimeout(DDM._addListeners, 10);
21169                     if (document && document.body) {
21170                         DDM._timeoutCount += 1;
21171                     }
21172                 }
21173             }
21174         },
21175
21176         /**
21177          * Recursively searches the immediate parent and all child nodes for
21178          * the handle element in order to determine wheter or not it was
21179          * clicked.
21180          * @method handleWasClicked
21181          * @param node the html element to inspect
21182          * @static
21183          */
21184         handleWasClicked: function(node, id) {
21185             if (this.isHandle(id, node.id)) {
21186                 return true;
21187             } else {
21188                 // check to see if this is a text node child of the one we want
21189                 var p = node.parentNode;
21190
21191                 while (p) {
21192                     if (this.isHandle(id, p.id)) {
21193                         return true;
21194                     } else {
21195                         p = p.parentNode;
21196                     }
21197                 }
21198             }
21199
21200             return false;
21201         }
21202
21203     };
21204
21205 }();
21206
21207 // shorter alias, save a few bytes
21208 Roo.dd.DDM = Roo.dd.DragDropMgr;
21209 Roo.dd.DDM._addListeners();
21210
21211 }/*
21212  * Based on:
21213  * Ext JS Library 1.1.1
21214  * Copyright(c) 2006-2007, Ext JS, LLC.
21215  *
21216  * Originally Released Under LGPL - original licence link has changed is not relivant.
21217  *
21218  * Fork - LGPL
21219  * <script type="text/javascript">
21220  */
21221
21222 /**
21223  * @class Roo.dd.DD
21224  * A DragDrop implementation where the linked element follows the
21225  * mouse cursor during a drag.
21226  * @extends Roo.dd.DragDrop
21227  * @constructor
21228  * @param {String} id the id of the linked element
21229  * @param {String} sGroup the group of related DragDrop items
21230  * @param {object} config an object containing configurable attributes
21231  *                Valid properties for DD:
21232  *                    scroll
21233  */
21234 Roo.dd.DD = function(id, sGroup, config) {
21235     if (id) {
21236         this.init(id, sGroup, config);
21237     }
21238 };
21239
21240 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21241
21242     /**
21243      * When set to true, the utility automatically tries to scroll the browser
21244      * window wehn a drag and drop element is dragged near the viewport boundary.
21245      * Defaults to true.
21246      * @property scroll
21247      * @type boolean
21248      */
21249     scroll: true,
21250
21251     /**
21252      * Sets the pointer offset to the distance between the linked element's top
21253      * left corner and the location the element was clicked
21254      * @method autoOffset
21255      * @param {int} iPageX the X coordinate of the click
21256      * @param {int} iPageY the Y coordinate of the click
21257      */
21258     autoOffset: function(iPageX, iPageY) {
21259         var x = iPageX - this.startPageX;
21260         var y = iPageY - this.startPageY;
21261         this.setDelta(x, y);
21262     },
21263
21264     /**
21265      * Sets the pointer offset.  You can call this directly to force the
21266      * offset to be in a particular location (e.g., pass in 0,0 to set it
21267      * to the center of the object)
21268      * @method setDelta
21269      * @param {int} iDeltaX the distance from the left
21270      * @param {int} iDeltaY the distance from the top
21271      */
21272     setDelta: function(iDeltaX, iDeltaY) {
21273         this.deltaX = iDeltaX;
21274         this.deltaY = iDeltaY;
21275     },
21276
21277     /**
21278      * Sets the drag element to the location of the mousedown or click event,
21279      * maintaining the cursor location relative to the location on the element
21280      * that was clicked.  Override this if you want to place the element in a
21281      * location other than where the cursor is.
21282      * @method setDragElPos
21283      * @param {int} iPageX the X coordinate of the mousedown or drag event
21284      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21285      */
21286     setDragElPos: function(iPageX, iPageY) {
21287         // the first time we do this, we are going to check to make sure
21288         // the element has css positioning
21289
21290         var el = this.getDragEl();
21291         this.alignElWithMouse(el, iPageX, iPageY);
21292     },
21293
21294     /**
21295      * Sets the element to the location of the mousedown or click event,
21296      * maintaining the cursor location relative to the location on the element
21297      * that was clicked.  Override this if you want to place the element in a
21298      * location other than where the cursor is.
21299      * @method alignElWithMouse
21300      * @param {HTMLElement} el the element to move
21301      * @param {int} iPageX the X coordinate of the mousedown or drag event
21302      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21303      */
21304     alignElWithMouse: function(el, iPageX, iPageY) {
21305         var oCoord = this.getTargetCoord(iPageX, iPageY);
21306         var fly = el.dom ? el : Roo.fly(el);
21307         if (!this.deltaSetXY) {
21308             var aCoord = [oCoord.x, oCoord.y];
21309             fly.setXY(aCoord);
21310             var newLeft = fly.getLeft(true);
21311             var newTop  = fly.getTop(true);
21312             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21313         } else {
21314             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21315         }
21316
21317         this.cachePosition(oCoord.x, oCoord.y);
21318         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21319         return oCoord;
21320     },
21321
21322     /**
21323      * Saves the most recent position so that we can reset the constraints and
21324      * tick marks on-demand.  We need to know this so that we can calculate the
21325      * number of pixels the element is offset from its original position.
21326      * @method cachePosition
21327      * @param iPageX the current x position (optional, this just makes it so we
21328      * don't have to look it up again)
21329      * @param iPageY the current y position (optional, this just makes it so we
21330      * don't have to look it up again)
21331      */
21332     cachePosition: function(iPageX, iPageY) {
21333         if (iPageX) {
21334             this.lastPageX = iPageX;
21335             this.lastPageY = iPageY;
21336         } else {
21337             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21338             this.lastPageX = aCoord[0];
21339             this.lastPageY = aCoord[1];
21340         }
21341     },
21342
21343     /**
21344      * Auto-scroll the window if the dragged object has been moved beyond the
21345      * visible window boundary.
21346      * @method autoScroll
21347      * @param {int} x the drag element's x position
21348      * @param {int} y the drag element's y position
21349      * @param {int} h the height of the drag element
21350      * @param {int} w the width of the drag element
21351      * @private
21352      */
21353     autoScroll: function(x, y, h, w) {
21354
21355         if (this.scroll) {
21356             // The client height
21357             var clientH = Roo.lib.Dom.getViewWidth();
21358
21359             // The client width
21360             var clientW = Roo.lib.Dom.getViewHeight();
21361
21362             // The amt scrolled down
21363             var st = this.DDM.getScrollTop();
21364
21365             // The amt scrolled right
21366             var sl = this.DDM.getScrollLeft();
21367
21368             // Location of the bottom of the element
21369             var bot = h + y;
21370
21371             // Location of the right of the element
21372             var right = w + x;
21373
21374             // The distance from the cursor to the bottom of the visible area,
21375             // adjusted so that we don't scroll if the cursor is beyond the
21376             // element drag constraints
21377             var toBot = (clientH + st - y - this.deltaY);
21378
21379             // The distance from the cursor to the right of the visible area
21380             var toRight = (clientW + sl - x - this.deltaX);
21381
21382
21383             // How close to the edge the cursor must be before we scroll
21384             // var thresh = (document.all) ? 100 : 40;
21385             var thresh = 40;
21386
21387             // How many pixels to scroll per autoscroll op.  This helps to reduce
21388             // clunky scrolling. IE is more sensitive about this ... it needs this
21389             // value to be higher.
21390             var scrAmt = (document.all) ? 80 : 30;
21391
21392             // Scroll down if we are near the bottom of the visible page and the
21393             // obj extends below the crease
21394             if ( bot > clientH && toBot < thresh ) {
21395                 window.scrollTo(sl, st + scrAmt);
21396             }
21397
21398             // Scroll up if the window is scrolled down and the top of the object
21399             // goes above the top border
21400             if ( y < st && st > 0 && y - st < thresh ) {
21401                 window.scrollTo(sl, st - scrAmt);
21402             }
21403
21404             // Scroll right if the obj is beyond the right border and the cursor is
21405             // near the border.
21406             if ( right > clientW && toRight < thresh ) {
21407                 window.scrollTo(sl + scrAmt, st);
21408             }
21409
21410             // Scroll left if the window has been scrolled to the right and the obj
21411             // extends past the left border
21412             if ( x < sl && sl > 0 && x - sl < thresh ) {
21413                 window.scrollTo(sl - scrAmt, st);
21414             }
21415         }
21416     },
21417
21418     /**
21419      * Finds the location the element should be placed if we want to move
21420      * it to where the mouse location less the click offset would place us.
21421      * @method getTargetCoord
21422      * @param {int} iPageX the X coordinate of the click
21423      * @param {int} iPageY the Y coordinate of the click
21424      * @return an object that contains the coordinates (Object.x and Object.y)
21425      * @private
21426      */
21427     getTargetCoord: function(iPageX, iPageY) {
21428
21429
21430         var x = iPageX - this.deltaX;
21431         var y = iPageY - this.deltaY;
21432
21433         if (this.constrainX) {
21434             if (x < this.minX) { x = this.minX; }
21435             if (x > this.maxX) { x = this.maxX; }
21436         }
21437
21438         if (this.constrainY) {
21439             if (y < this.minY) { y = this.minY; }
21440             if (y > this.maxY) { y = this.maxY; }
21441         }
21442
21443         x = this.getTick(x, this.xTicks);
21444         y = this.getTick(y, this.yTicks);
21445
21446
21447         return {x:x, y:y};
21448     },
21449
21450     /*
21451      * Sets up config options specific to this class. Overrides
21452      * Roo.dd.DragDrop, but all versions of this method through the
21453      * inheritance chain are called
21454      */
21455     applyConfig: function() {
21456         Roo.dd.DD.superclass.applyConfig.call(this);
21457         this.scroll = (this.config.scroll !== false);
21458     },
21459
21460     /*
21461      * Event that fires prior to the onMouseDown event.  Overrides
21462      * Roo.dd.DragDrop.
21463      */
21464     b4MouseDown: function(e) {
21465         // this.resetConstraints();
21466         this.autoOffset(e.getPageX(),
21467                             e.getPageY());
21468     },
21469
21470     /*
21471      * Event that fires prior to the onDrag event.  Overrides
21472      * Roo.dd.DragDrop.
21473      */
21474     b4Drag: function(e) {
21475         this.setDragElPos(e.getPageX(),
21476                             e.getPageY());
21477     },
21478
21479     toString: function() {
21480         return ("DD " + this.id);
21481     }
21482
21483     //////////////////////////////////////////////////////////////////////////
21484     // Debugging ygDragDrop events that can be overridden
21485     //////////////////////////////////////////////////////////////////////////
21486     /*
21487     startDrag: function(x, y) {
21488     },
21489
21490     onDrag: function(e) {
21491     },
21492
21493     onDragEnter: function(e, id) {
21494     },
21495
21496     onDragOver: function(e, id) {
21497     },
21498
21499     onDragOut: function(e, id) {
21500     },
21501
21502     onDragDrop: function(e, id) {
21503     },
21504
21505     endDrag: function(e) {
21506     }
21507
21508     */
21509
21510 });/*
21511  * Based on:
21512  * Ext JS Library 1.1.1
21513  * Copyright(c) 2006-2007, Ext JS, LLC.
21514  *
21515  * Originally Released Under LGPL - original licence link has changed is not relivant.
21516  *
21517  * Fork - LGPL
21518  * <script type="text/javascript">
21519  */
21520
21521 /**
21522  * @class Roo.dd.DDProxy
21523  * A DragDrop implementation that inserts an empty, bordered div into
21524  * the document that follows the cursor during drag operations.  At the time of
21525  * the click, the frame div is resized to the dimensions of the linked html
21526  * element, and moved to the exact location of the linked element.
21527  *
21528  * References to the "frame" element refer to the single proxy element that
21529  * was created to be dragged in place of all DDProxy elements on the
21530  * page.
21531  *
21532  * @extends Roo.dd.DD
21533  * @constructor
21534  * @param {String} id the id of the linked html element
21535  * @param {String} sGroup the group of related DragDrop objects
21536  * @param {object} config an object containing configurable attributes
21537  *                Valid properties for DDProxy in addition to those in DragDrop:
21538  *                   resizeFrame, centerFrame, dragElId
21539  */
21540 Roo.dd.DDProxy = function(id, sGroup, config) {
21541     if (id) {
21542         this.init(id, sGroup, config);
21543         this.initFrame();
21544     }
21545 };
21546
21547 /**
21548  * The default drag frame div id
21549  * @property Roo.dd.DDProxy.dragElId
21550  * @type String
21551  * @static
21552  */
21553 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21554
21555 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21556
21557     /**
21558      * By default we resize the drag frame to be the same size as the element
21559      * we want to drag (this is to get the frame effect).  We can turn it off
21560      * if we want a different behavior.
21561      * @property resizeFrame
21562      * @type boolean
21563      */
21564     resizeFrame: true,
21565
21566     /**
21567      * By default the frame is positioned exactly where the drag element is, so
21568      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21569      * you do not have constraints on the obj is to have the drag frame centered
21570      * around the cursor.  Set centerFrame to true for this effect.
21571      * @property centerFrame
21572      * @type boolean
21573      */
21574     centerFrame: false,
21575
21576     /**
21577      * Creates the proxy element if it does not yet exist
21578      * @method createFrame
21579      */
21580     createFrame: function() {
21581         var self = this;
21582         var body = document.body;
21583
21584         if (!body || !body.firstChild) {
21585             setTimeout( function() { self.createFrame(); }, 50 );
21586             return;
21587         }
21588
21589         var div = this.getDragEl();
21590
21591         if (!div) {
21592             div    = document.createElement("div");
21593             div.id = this.dragElId;
21594             var s  = div.style;
21595
21596             s.position   = "absolute";
21597             s.visibility = "hidden";
21598             s.cursor     = "move";
21599             s.border     = "2px solid #aaa";
21600             s.zIndex     = 999;
21601
21602             // appendChild can blow up IE if invoked prior to the window load event
21603             // while rendering a table.  It is possible there are other scenarios
21604             // that would cause this to happen as well.
21605             body.insertBefore(div, body.firstChild);
21606         }
21607     },
21608
21609     /**
21610      * Initialization for the drag frame element.  Must be called in the
21611      * constructor of all subclasses
21612      * @method initFrame
21613      */
21614     initFrame: function() {
21615         this.createFrame();
21616     },
21617
21618     applyConfig: function() {
21619         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21620
21621         this.resizeFrame = (this.config.resizeFrame !== false);
21622         this.centerFrame = (this.config.centerFrame);
21623         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21624     },
21625
21626     /**
21627      * Resizes the drag frame to the dimensions of the clicked object, positions
21628      * it over the object, and finally displays it
21629      * @method showFrame
21630      * @param {int} iPageX X click position
21631      * @param {int} iPageY Y click position
21632      * @private
21633      */
21634     showFrame: function(iPageX, iPageY) {
21635         var el = this.getEl();
21636         var dragEl = this.getDragEl();
21637         var s = dragEl.style;
21638
21639         this._resizeProxy();
21640
21641         if (this.centerFrame) {
21642             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21643                            Math.round(parseInt(s.height, 10)/2) );
21644         }
21645
21646         this.setDragElPos(iPageX, iPageY);
21647
21648         Roo.fly(dragEl).show();
21649     },
21650
21651     /**
21652      * The proxy is automatically resized to the dimensions of the linked
21653      * element when a drag is initiated, unless resizeFrame is set to false
21654      * @method _resizeProxy
21655      * @private
21656      */
21657     _resizeProxy: function() {
21658         if (this.resizeFrame) {
21659             var el = this.getEl();
21660             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21661         }
21662     },
21663
21664     // overrides Roo.dd.DragDrop
21665     b4MouseDown: function(e) {
21666         var x = e.getPageX();
21667         var y = e.getPageY();
21668         this.autoOffset(x, y);
21669         this.setDragElPos(x, y);
21670     },
21671
21672     // overrides Roo.dd.DragDrop
21673     b4StartDrag: function(x, y) {
21674         // show the drag frame
21675         this.showFrame(x, y);
21676     },
21677
21678     // overrides Roo.dd.DragDrop
21679     b4EndDrag: function(e) {
21680         Roo.fly(this.getDragEl()).hide();
21681     },
21682
21683     // overrides Roo.dd.DragDrop
21684     // By default we try to move the element to the last location of the frame.
21685     // This is so that the default behavior mirrors that of Roo.dd.DD.
21686     endDrag: function(e) {
21687
21688         var lel = this.getEl();
21689         var del = this.getDragEl();
21690
21691         // Show the drag frame briefly so we can get its position
21692         del.style.visibility = "";
21693
21694         this.beforeMove();
21695         // Hide the linked element before the move to get around a Safari
21696         // rendering bug.
21697         lel.style.visibility = "hidden";
21698         Roo.dd.DDM.moveToEl(lel, del);
21699         del.style.visibility = "hidden";
21700         lel.style.visibility = "";
21701
21702         this.afterDrag();
21703     },
21704
21705     beforeMove : function(){
21706
21707     },
21708
21709     afterDrag : function(){
21710
21711     },
21712
21713     toString: function() {
21714         return ("DDProxy " + this.id);
21715     }
21716
21717 });
21718 /*
21719  * Based on:
21720  * Ext JS Library 1.1.1
21721  * Copyright(c) 2006-2007, Ext JS, LLC.
21722  *
21723  * Originally Released Under LGPL - original licence link has changed is not relivant.
21724  *
21725  * Fork - LGPL
21726  * <script type="text/javascript">
21727  */
21728
21729  /**
21730  * @class Roo.dd.DDTarget
21731  * A DragDrop implementation that does not move, but can be a drop
21732  * target.  You would get the same result by simply omitting implementation
21733  * for the event callbacks, but this way we reduce the processing cost of the
21734  * event listener and the callbacks.
21735  * @extends Roo.dd.DragDrop
21736  * @constructor
21737  * @param {String} id the id of the element that is a drop target
21738  * @param {String} sGroup the group of related DragDrop objects
21739  * @param {object} config an object containing configurable attributes
21740  *                 Valid properties for DDTarget in addition to those in
21741  *                 DragDrop:
21742  *                    none
21743  */
21744 Roo.dd.DDTarget = function(id, sGroup, config) {
21745     if (id) {
21746         this.initTarget(id, sGroup, config);
21747     }
21748     if (config && (config.listeners || config.events)) { 
21749         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21750             listeners : config.listeners || {}, 
21751             events : config.events || {} 
21752         });    
21753     }
21754 };
21755
21756 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21757 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21758     toString: function() {
21759         return ("DDTarget " + this.id);
21760     }
21761 });
21762 /*
21763  * Based on:
21764  * Ext JS Library 1.1.1
21765  * Copyright(c) 2006-2007, Ext JS, LLC.
21766  *
21767  * Originally Released Under LGPL - original licence link has changed is not relivant.
21768  *
21769  * Fork - LGPL
21770  * <script type="text/javascript">
21771  */
21772  
21773
21774 /**
21775  * @class Roo.dd.ScrollManager
21776  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21777  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21778  * @singleton
21779  */
21780 Roo.dd.ScrollManager = function(){
21781     var ddm = Roo.dd.DragDropMgr;
21782     var els = {};
21783     var dragEl = null;
21784     var proc = {};
21785     
21786     
21787     
21788     var onStop = function(e){
21789         dragEl = null;
21790         clearProc();
21791     };
21792     
21793     var triggerRefresh = function(){
21794         if(ddm.dragCurrent){
21795              ddm.refreshCache(ddm.dragCurrent.groups);
21796         }
21797     };
21798     
21799     var doScroll = function(){
21800         if(ddm.dragCurrent){
21801             var dds = Roo.dd.ScrollManager;
21802             if(!dds.animate){
21803                 if(proc.el.scroll(proc.dir, dds.increment)){
21804                     triggerRefresh();
21805                 }
21806             }else{
21807                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21808             }
21809         }
21810     };
21811     
21812     var clearProc = function(){
21813         if(proc.id){
21814             clearInterval(proc.id);
21815         }
21816         proc.id = 0;
21817         proc.el = null;
21818         proc.dir = "";
21819     };
21820     
21821     var startProc = function(el, dir){
21822          Roo.log('scroll startproc');
21823         clearProc();
21824         proc.el = el;
21825         proc.dir = dir;
21826         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21827     };
21828     
21829     var onFire = function(e, isDrop){
21830        
21831         if(isDrop || !ddm.dragCurrent){ return; }
21832         var dds = Roo.dd.ScrollManager;
21833         if(!dragEl || dragEl != ddm.dragCurrent){
21834             dragEl = ddm.dragCurrent;
21835             // refresh regions on drag start
21836             dds.refreshCache();
21837         }
21838         
21839         var xy = Roo.lib.Event.getXY(e);
21840         var pt = new Roo.lib.Point(xy[0], xy[1]);
21841         for(var id in els){
21842             var el = els[id], r = el._region;
21843             if(r && r.contains(pt) && el.isScrollable()){
21844                 if(r.bottom - pt.y <= dds.thresh){
21845                     if(proc.el != el){
21846                         startProc(el, "down");
21847                     }
21848                     return;
21849                 }else if(r.right - pt.x <= dds.thresh){
21850                     if(proc.el != el){
21851                         startProc(el, "left");
21852                     }
21853                     return;
21854                 }else if(pt.y - r.top <= dds.thresh){
21855                     if(proc.el != el){
21856                         startProc(el, "up");
21857                     }
21858                     return;
21859                 }else if(pt.x - r.left <= dds.thresh){
21860                     if(proc.el != el){
21861                         startProc(el, "right");
21862                     }
21863                     return;
21864                 }
21865             }
21866         }
21867         clearProc();
21868     };
21869     
21870     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21871     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21872     
21873     return {
21874         /**
21875          * Registers new overflow element(s) to auto scroll
21876          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21877          */
21878         register : function(el){
21879             if(el instanceof Array){
21880                 for(var i = 0, len = el.length; i < len; i++) {
21881                         this.register(el[i]);
21882                 }
21883             }else{
21884                 el = Roo.get(el);
21885                 els[el.id] = el;
21886             }
21887             Roo.dd.ScrollManager.els = els;
21888         },
21889         
21890         /**
21891          * Unregisters overflow element(s) so they are no longer scrolled
21892          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21893          */
21894         unregister : function(el){
21895             if(el instanceof Array){
21896                 for(var i = 0, len = el.length; i < len; i++) {
21897                         this.unregister(el[i]);
21898                 }
21899             }else{
21900                 el = Roo.get(el);
21901                 delete els[el.id];
21902             }
21903         },
21904         
21905         /**
21906          * The number of pixels from the edge of a container the pointer needs to be to 
21907          * trigger scrolling (defaults to 25)
21908          * @type Number
21909          */
21910         thresh : 25,
21911         
21912         /**
21913          * The number of pixels to scroll in each scroll increment (defaults to 50)
21914          * @type Number
21915          */
21916         increment : 100,
21917         
21918         /**
21919          * The frequency of scrolls in milliseconds (defaults to 500)
21920          * @type Number
21921          */
21922         frequency : 500,
21923         
21924         /**
21925          * True to animate the scroll (defaults to true)
21926          * @type Boolean
21927          */
21928         animate: true,
21929         
21930         /**
21931          * The animation duration in seconds - 
21932          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21933          * @type Number
21934          */
21935         animDuration: .4,
21936         
21937         /**
21938          * Manually trigger a cache refresh.
21939          */
21940         refreshCache : function(){
21941             for(var id in els){
21942                 if(typeof els[id] == 'object'){ // for people extending the object prototype
21943                     els[id]._region = els[id].getRegion();
21944                 }
21945             }
21946         }
21947     };
21948 }();/*
21949  * Based on:
21950  * Ext JS Library 1.1.1
21951  * Copyright(c) 2006-2007, Ext JS, LLC.
21952  *
21953  * Originally Released Under LGPL - original licence link has changed is not relivant.
21954  *
21955  * Fork - LGPL
21956  * <script type="text/javascript">
21957  */
21958  
21959
21960 /**
21961  * @class Roo.dd.Registry
21962  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
21963  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
21964  * @singleton
21965  */
21966 Roo.dd.Registry = function(){
21967     var elements = {}; 
21968     var handles = {}; 
21969     var autoIdSeed = 0;
21970
21971     var getId = function(el, autogen){
21972         if(typeof el == "string"){
21973             return el;
21974         }
21975         var id = el.id;
21976         if(!id && autogen !== false){
21977             id = "roodd-" + (++autoIdSeed);
21978             el.id = id;
21979         }
21980         return id;
21981     };
21982     
21983     return {
21984     /**
21985      * Register a drag drop element
21986      * @param {String|HTMLElement} element The id or DOM node to register
21987      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
21988      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
21989      * knows how to interpret, plus there are some specific properties known to the Registry that should be
21990      * populated in the data object (if applicable):
21991      * <pre>
21992 Value      Description<br />
21993 ---------  ------------------------------------------<br />
21994 handles    Array of DOM nodes that trigger dragging<br />
21995            for the element being registered<br />
21996 isHandle   True if the element passed in triggers<br />
21997            dragging itself, else false
21998 </pre>
21999      */
22000         register : function(el, data){
22001             data = data || {};
22002             if(typeof el == "string"){
22003                 el = document.getElementById(el);
22004             }
22005             data.ddel = el;
22006             elements[getId(el)] = data;
22007             if(data.isHandle !== false){
22008                 handles[data.ddel.id] = data;
22009             }
22010             if(data.handles){
22011                 var hs = data.handles;
22012                 for(var i = 0, len = hs.length; i < len; i++){
22013                         handles[getId(hs[i])] = data;
22014                 }
22015             }
22016         },
22017
22018     /**
22019      * Unregister a drag drop element
22020      * @param {String|HTMLElement}  element The id or DOM node to unregister
22021      */
22022         unregister : function(el){
22023             var id = getId(el, false);
22024             var data = elements[id];
22025             if(data){
22026                 delete elements[id];
22027                 if(data.handles){
22028                     var hs = data.handles;
22029                     for(var i = 0, len = hs.length; i < len; i++){
22030                         delete handles[getId(hs[i], false)];
22031                     }
22032                 }
22033             }
22034         },
22035
22036     /**
22037      * Returns the handle registered for a DOM Node by id
22038      * @param {String|HTMLElement} id The DOM node or id to look up
22039      * @return {Object} handle The custom handle data
22040      */
22041         getHandle : function(id){
22042             if(typeof id != "string"){ // must be element?
22043                 id = id.id;
22044             }
22045             return handles[id];
22046         },
22047
22048     /**
22049      * Returns the handle that is registered for the DOM node that is the target of the event
22050      * @param {Event} e The event
22051      * @return {Object} handle The custom handle data
22052      */
22053         getHandleFromEvent : function(e){
22054             var t = Roo.lib.Event.getTarget(e);
22055             return t ? handles[t.id] : null;
22056         },
22057
22058     /**
22059      * Returns a custom data object that is registered for a DOM node by id
22060      * @param {String|HTMLElement} id The DOM node or id to look up
22061      * @return {Object} data The custom data
22062      */
22063         getTarget : function(id){
22064             if(typeof id != "string"){ // must be element?
22065                 id = id.id;
22066             }
22067             return elements[id];
22068         },
22069
22070     /**
22071      * Returns a custom data object that is registered for the DOM node that is the target of the event
22072      * @param {Event} e The event
22073      * @return {Object} data The custom data
22074      */
22075         getTargetFromEvent : function(e){
22076             var t = Roo.lib.Event.getTarget(e);
22077             return t ? elements[t.id] || handles[t.id] : null;
22078         }
22079     };
22080 }();/*
22081  * Based on:
22082  * Ext JS Library 1.1.1
22083  * Copyright(c) 2006-2007, Ext JS, LLC.
22084  *
22085  * Originally Released Under LGPL - original licence link has changed is not relivant.
22086  *
22087  * Fork - LGPL
22088  * <script type="text/javascript">
22089  */
22090  
22091
22092 /**
22093  * @class Roo.dd.StatusProxy
22094  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22095  * default drag proxy used by all Roo.dd components.
22096  * @constructor
22097  * @param {Object} config
22098  */
22099 Roo.dd.StatusProxy = function(config){
22100     Roo.apply(this, config);
22101     this.id = this.id || Roo.id();
22102     this.el = new Roo.Layer({
22103         dh: {
22104             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22105                 {tag: "div", cls: "x-dd-drop-icon"},
22106                 {tag: "div", cls: "x-dd-drag-ghost"}
22107             ]
22108         }, 
22109         shadow: !config || config.shadow !== false
22110     });
22111     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22112     this.dropStatus = this.dropNotAllowed;
22113 };
22114
22115 Roo.dd.StatusProxy.prototype = {
22116     /**
22117      * @cfg {String} dropAllowed
22118      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22119      */
22120     dropAllowed : "x-dd-drop-ok",
22121     /**
22122      * @cfg {String} dropNotAllowed
22123      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22124      */
22125     dropNotAllowed : "x-dd-drop-nodrop",
22126
22127     /**
22128      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22129      * over the current target element.
22130      * @param {String} cssClass The css class for the new drop status indicator image
22131      */
22132     setStatus : function(cssClass){
22133         cssClass = cssClass || this.dropNotAllowed;
22134         if(this.dropStatus != cssClass){
22135             this.el.replaceClass(this.dropStatus, cssClass);
22136             this.dropStatus = cssClass;
22137         }
22138     },
22139
22140     /**
22141      * Resets the status indicator to the default dropNotAllowed value
22142      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22143      */
22144     reset : function(clearGhost){
22145         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22146         this.dropStatus = this.dropNotAllowed;
22147         if(clearGhost){
22148             this.ghost.update("");
22149         }
22150     },
22151
22152     /**
22153      * Updates the contents of the ghost element
22154      * @param {String} html The html that will replace the current innerHTML of the ghost element
22155      */
22156     update : function(html){
22157         if(typeof html == "string"){
22158             this.ghost.update(html);
22159         }else{
22160             this.ghost.update("");
22161             html.style.margin = "0";
22162             this.ghost.dom.appendChild(html);
22163         }
22164         // ensure float = none set?? cant remember why though.
22165         var el = this.ghost.dom.firstChild;
22166                 if(el){
22167                         Roo.fly(el).setStyle('float', 'none');
22168                 }
22169     },
22170     
22171     /**
22172      * Returns the underlying proxy {@link Roo.Layer}
22173      * @return {Roo.Layer} el
22174     */
22175     getEl : function(){
22176         return this.el;
22177     },
22178
22179     /**
22180      * Returns the ghost element
22181      * @return {Roo.Element} el
22182      */
22183     getGhost : function(){
22184         return this.ghost;
22185     },
22186
22187     /**
22188      * Hides the proxy
22189      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22190      */
22191     hide : function(clear){
22192         this.el.hide();
22193         if(clear){
22194             this.reset(true);
22195         }
22196     },
22197
22198     /**
22199      * Stops the repair animation if it's currently running
22200      */
22201     stop : function(){
22202         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22203             this.anim.stop();
22204         }
22205     },
22206
22207     /**
22208      * Displays this proxy
22209      */
22210     show : function(){
22211         this.el.show();
22212     },
22213
22214     /**
22215      * Force the Layer to sync its shadow and shim positions to the element
22216      */
22217     sync : function(){
22218         this.el.sync();
22219     },
22220
22221     /**
22222      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22223      * invalid drop operation by the item being dragged.
22224      * @param {Array} xy The XY position of the element ([x, y])
22225      * @param {Function} callback The function to call after the repair is complete
22226      * @param {Object} scope The scope in which to execute the callback
22227      */
22228     repair : function(xy, callback, scope){
22229         this.callback = callback;
22230         this.scope = scope;
22231         if(xy && this.animRepair !== false){
22232             this.el.addClass("x-dd-drag-repair");
22233             this.el.hideUnders(true);
22234             this.anim = this.el.shift({
22235                 duration: this.repairDuration || .5,
22236                 easing: 'easeOut',
22237                 xy: xy,
22238                 stopFx: true,
22239                 callback: this.afterRepair,
22240                 scope: this
22241             });
22242         }else{
22243             this.afterRepair();
22244         }
22245     },
22246
22247     // private
22248     afterRepair : function(){
22249         this.hide(true);
22250         if(typeof this.callback == "function"){
22251             this.callback.call(this.scope || this);
22252         }
22253         this.callback = null;
22254         this.scope = null;
22255     }
22256 };/*
22257  * Based on:
22258  * Ext JS Library 1.1.1
22259  * Copyright(c) 2006-2007, Ext JS, LLC.
22260  *
22261  * Originally Released Under LGPL - original licence link has changed is not relivant.
22262  *
22263  * Fork - LGPL
22264  * <script type="text/javascript">
22265  */
22266
22267 /**
22268  * @class Roo.dd.DragSource
22269  * @extends Roo.dd.DDProxy
22270  * A simple class that provides the basic implementation needed to make any element draggable.
22271  * @constructor
22272  * @param {String/HTMLElement/Element} el The container element
22273  * @param {Object} config
22274  */
22275 Roo.dd.DragSource = function(el, config){
22276     this.el = Roo.get(el);
22277     this.dragData = {};
22278     
22279     Roo.apply(this, config);
22280     
22281     if(!this.proxy){
22282         this.proxy = new Roo.dd.StatusProxy();
22283     }
22284
22285     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22286           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22287     
22288     this.dragging = false;
22289 };
22290
22291 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22292     /**
22293      * @cfg {String} dropAllowed
22294      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22295      */
22296     dropAllowed : "x-dd-drop-ok",
22297     /**
22298      * @cfg {String} dropNotAllowed
22299      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22300      */
22301     dropNotAllowed : "x-dd-drop-nodrop",
22302
22303     /**
22304      * Returns the data object associated with this drag source
22305      * @return {Object} data An object containing arbitrary data
22306      */
22307     getDragData : function(e){
22308         return this.dragData;
22309     },
22310
22311     // private
22312     onDragEnter : function(e, id){
22313         var target = Roo.dd.DragDropMgr.getDDById(id);
22314         this.cachedTarget = target;
22315         if(this.beforeDragEnter(target, e, id) !== false){
22316             if(target.isNotifyTarget){
22317                 var status = target.notifyEnter(this, e, this.dragData);
22318                 this.proxy.setStatus(status);
22319             }else{
22320                 this.proxy.setStatus(this.dropAllowed);
22321             }
22322             
22323             if(this.afterDragEnter){
22324                 /**
22325                  * An empty function by default, but provided so that you can perform a custom action
22326                  * when the dragged item enters the drop target by providing an implementation.
22327                  * @param {Roo.dd.DragDrop} target The drop target
22328                  * @param {Event} e The event object
22329                  * @param {String} id The id of the dragged element
22330                  * @method afterDragEnter
22331                  */
22332                 this.afterDragEnter(target, e, id);
22333             }
22334         }
22335     },
22336
22337     /**
22338      * An empty function by default, but provided so that you can perform a custom action
22339      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22340      * @param {Roo.dd.DragDrop} target The drop target
22341      * @param {Event} e The event object
22342      * @param {String} id The id of the dragged element
22343      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22344      */
22345     beforeDragEnter : function(target, e, id){
22346         return true;
22347     },
22348
22349     // private
22350     alignElWithMouse: function() {
22351         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22352         this.proxy.sync();
22353     },
22354
22355     // private
22356     onDragOver : function(e, id){
22357         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22358         if(this.beforeDragOver(target, e, id) !== false){
22359             if(target.isNotifyTarget){
22360                 var status = target.notifyOver(this, e, this.dragData);
22361                 this.proxy.setStatus(status);
22362             }
22363
22364             if(this.afterDragOver){
22365                 /**
22366                  * An empty function by default, but provided so that you can perform a custom action
22367                  * while the dragged item is over the drop target by providing an implementation.
22368                  * @param {Roo.dd.DragDrop} target The drop target
22369                  * @param {Event} e The event object
22370                  * @param {String} id The id of the dragged element
22371                  * @method afterDragOver
22372                  */
22373                 this.afterDragOver(target, e, id);
22374             }
22375         }
22376     },
22377
22378     /**
22379      * An empty function by default, but provided so that you can perform a custom action
22380      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22381      * @param {Roo.dd.DragDrop} target The drop target
22382      * @param {Event} e The event object
22383      * @param {String} id The id of the dragged element
22384      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22385      */
22386     beforeDragOver : function(target, e, id){
22387         return true;
22388     },
22389
22390     // private
22391     onDragOut : function(e, id){
22392         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22393         if(this.beforeDragOut(target, e, id) !== false){
22394             if(target.isNotifyTarget){
22395                 target.notifyOut(this, e, this.dragData);
22396             }
22397             this.proxy.reset();
22398             if(this.afterDragOut){
22399                 /**
22400                  * An empty function by default, but provided so that you can perform a custom action
22401                  * after the dragged item is dragged out of the target without dropping.
22402                  * @param {Roo.dd.DragDrop} target The drop target
22403                  * @param {Event} e The event object
22404                  * @param {String} id The id of the dragged element
22405                  * @method afterDragOut
22406                  */
22407                 this.afterDragOut(target, e, id);
22408             }
22409         }
22410         this.cachedTarget = null;
22411     },
22412
22413     /**
22414      * An empty function by default, but provided so that you can perform a custom action before the dragged
22415      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22416      * @param {Roo.dd.DragDrop} target The drop target
22417      * @param {Event} e The event object
22418      * @param {String} id The id of the dragged element
22419      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22420      */
22421     beforeDragOut : function(target, e, id){
22422         return true;
22423     },
22424     
22425     // private
22426     onDragDrop : function(e, id){
22427         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22428         if(this.beforeDragDrop(target, e, id) !== false){
22429             if(target.isNotifyTarget){
22430                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22431                     this.onValidDrop(target, e, id);
22432                 }else{
22433                     this.onInvalidDrop(target, e, id);
22434                 }
22435             }else{
22436                 this.onValidDrop(target, e, id);
22437             }
22438             
22439             if(this.afterDragDrop){
22440                 /**
22441                  * An empty function by default, but provided so that you can perform a custom action
22442                  * after a valid drag drop has occurred by providing an implementation.
22443                  * @param {Roo.dd.DragDrop} target The drop target
22444                  * @param {Event} e The event object
22445                  * @param {String} id The id of the dropped element
22446                  * @method afterDragDrop
22447                  */
22448                 this.afterDragDrop(target, e, id);
22449             }
22450         }
22451         delete this.cachedTarget;
22452     },
22453
22454     /**
22455      * An empty function by default, but provided so that you can perform a custom action before the dragged
22456      * item is dropped onto the target and optionally cancel the onDragDrop.
22457      * @param {Roo.dd.DragDrop} target The drop target
22458      * @param {Event} e The event object
22459      * @param {String} id The id of the dragged element
22460      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22461      */
22462     beforeDragDrop : function(target, e, id){
22463         return true;
22464     },
22465
22466     // private
22467     onValidDrop : function(target, e, id){
22468         this.hideProxy();
22469         if(this.afterValidDrop){
22470             /**
22471              * An empty function by default, but provided so that you can perform a custom action
22472              * after a valid drop has occurred by providing an implementation.
22473              * @param {Object} target The target DD 
22474              * @param {Event} e The event object
22475              * @param {String} id The id of the dropped element
22476              * @method afterInvalidDrop
22477              */
22478             this.afterValidDrop(target, e, id);
22479         }
22480     },
22481
22482     // private
22483     getRepairXY : function(e, data){
22484         return this.el.getXY();  
22485     },
22486
22487     // private
22488     onInvalidDrop : function(target, e, id){
22489         this.beforeInvalidDrop(target, e, id);
22490         if(this.cachedTarget){
22491             if(this.cachedTarget.isNotifyTarget){
22492                 this.cachedTarget.notifyOut(this, e, this.dragData);
22493             }
22494             this.cacheTarget = null;
22495         }
22496         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22497
22498         if(this.afterInvalidDrop){
22499             /**
22500              * An empty function by default, but provided so that you can perform a custom action
22501              * after an invalid drop has occurred by providing an implementation.
22502              * @param {Event} e The event object
22503              * @param {String} id The id of the dropped element
22504              * @method afterInvalidDrop
22505              */
22506             this.afterInvalidDrop(e, id);
22507         }
22508     },
22509
22510     // private
22511     afterRepair : function(){
22512         if(Roo.enableFx){
22513             this.el.highlight(this.hlColor || "c3daf9");
22514         }
22515         this.dragging = false;
22516     },
22517
22518     /**
22519      * An empty function by default, but provided so that you can perform a custom action after an invalid
22520      * drop has occurred.
22521      * @param {Roo.dd.DragDrop} target The drop target
22522      * @param {Event} e The event object
22523      * @param {String} id The id of the dragged element
22524      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22525      */
22526     beforeInvalidDrop : function(target, e, id){
22527         return true;
22528     },
22529
22530     // private
22531     handleMouseDown : function(e){
22532         if(this.dragging) {
22533             return;
22534         }
22535         var data = this.getDragData(e);
22536         if(data && this.onBeforeDrag(data, e) !== false){
22537             this.dragData = data;
22538             this.proxy.stop();
22539             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22540         } 
22541     },
22542
22543     /**
22544      * An empty function by default, but provided so that you can perform a custom action before the initial
22545      * drag event begins and optionally cancel it.
22546      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22547      * @param {Event} e The event object
22548      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22549      */
22550     onBeforeDrag : function(data, e){
22551         return true;
22552     },
22553
22554     /**
22555      * An empty function by default, but provided so that you can perform a custom action once the initial
22556      * drag event has begun.  The drag cannot be canceled from this function.
22557      * @param {Number} x The x position of the click on the dragged object
22558      * @param {Number} y The y position of the click on the dragged object
22559      */
22560     onStartDrag : Roo.emptyFn,
22561
22562     // private - YUI override
22563     startDrag : function(x, y){
22564         this.proxy.reset();
22565         this.dragging = true;
22566         this.proxy.update("");
22567         this.onInitDrag(x, y);
22568         this.proxy.show();
22569     },
22570
22571     // private
22572     onInitDrag : function(x, y){
22573         var clone = this.el.dom.cloneNode(true);
22574         clone.id = Roo.id(); // prevent duplicate ids
22575         this.proxy.update(clone);
22576         this.onStartDrag(x, y);
22577         return true;
22578     },
22579
22580     /**
22581      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22582      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22583      */
22584     getProxy : function(){
22585         return this.proxy;  
22586     },
22587
22588     /**
22589      * Hides the drag source's {@link Roo.dd.StatusProxy}
22590      */
22591     hideProxy : function(){
22592         this.proxy.hide();  
22593         this.proxy.reset(true);
22594         this.dragging = false;
22595     },
22596
22597     // private
22598     triggerCacheRefresh : function(){
22599         Roo.dd.DDM.refreshCache(this.groups);
22600     },
22601
22602     // private - override to prevent hiding
22603     b4EndDrag: function(e) {
22604     },
22605
22606     // private - override to prevent moving
22607     endDrag : function(e){
22608         this.onEndDrag(this.dragData, e);
22609     },
22610
22611     // private
22612     onEndDrag : function(data, e){
22613     },
22614     
22615     // private - pin to cursor
22616     autoOffset : function(x, y) {
22617         this.setDelta(-12, -20);
22618     }    
22619 });/*
22620  * Based on:
22621  * Ext JS Library 1.1.1
22622  * Copyright(c) 2006-2007, Ext JS, LLC.
22623  *
22624  * Originally Released Under LGPL - original licence link has changed is not relivant.
22625  *
22626  * Fork - LGPL
22627  * <script type="text/javascript">
22628  */
22629
22630
22631 /**
22632  * @class Roo.dd.DropTarget
22633  * @extends Roo.dd.DDTarget
22634  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22635  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22636  * @constructor
22637  * @param {String/HTMLElement/Element} el The container element
22638  * @param {Object} config
22639  */
22640 Roo.dd.DropTarget = function(el, config){
22641     this.el = Roo.get(el);
22642     
22643     var listeners = false; ;
22644     if (config && config.listeners) {
22645         listeners= config.listeners;
22646         delete config.listeners;
22647     }
22648     Roo.apply(this, config);
22649     
22650     if(this.containerScroll){
22651         Roo.dd.ScrollManager.register(this.el);
22652     }
22653     this.addEvents( {
22654          /**
22655          * @scope Roo.dd.DropTarget
22656          */
22657          
22658          /**
22659          * @event enter
22660          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22661          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22662          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22663          * 
22664          * IMPORTANT : it should set  this.valid to true|false
22665          * 
22666          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22667          * @param {Event} e The event
22668          * @param {Object} data An object containing arbitrary data supplied by the drag source
22669          */
22670         "enter" : true,
22671         
22672          /**
22673          * @event over
22674          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22675          * This method will be called on every mouse movement while the drag source is over the drop target.
22676          * This default implementation simply returns the dropAllowed config value.
22677          * 
22678          * IMPORTANT : it should set  this.valid to true|false
22679          * 
22680          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22681          * @param {Event} e The event
22682          * @param {Object} data An object containing arbitrary data supplied by the drag source
22683          
22684          */
22685         "over" : true,
22686         /**
22687          * @event out
22688          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22689          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22690          * overClass (if any) from the drop element.
22691          * 
22692          * 
22693          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22694          * @param {Event} e The event
22695          * @param {Object} data An object containing arbitrary data supplied by the drag source
22696          */
22697          "out" : true,
22698          
22699         /**
22700          * @event drop
22701          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22702          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22703          * implementation that does something to process the drop event and returns true so that the drag source's
22704          * repair action does not run.
22705          * 
22706          * IMPORTANT : it should set this.success
22707          * 
22708          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22709          * @param {Event} e The event
22710          * @param {Object} data An object containing arbitrary data supplied by the drag source
22711         */
22712          "drop" : true
22713     });
22714             
22715      
22716     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22717         this.el.dom, 
22718         this.ddGroup || this.group,
22719         {
22720             isTarget: true,
22721             listeners : listeners || {} 
22722            
22723         
22724         }
22725     );
22726
22727 };
22728
22729 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22730     /**
22731      * @cfg {String} overClass
22732      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22733      */
22734      /**
22735      * @cfg {String} ddGroup
22736      * The drag drop group to handle drop events for
22737      */
22738      
22739     /**
22740      * @cfg {String} dropAllowed
22741      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22742      */
22743     dropAllowed : "x-dd-drop-ok",
22744     /**
22745      * @cfg {String} dropNotAllowed
22746      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22747      */
22748     dropNotAllowed : "x-dd-drop-nodrop",
22749     /**
22750      * @cfg {boolean} success
22751      * set this after drop listener.. 
22752      */
22753     success : false,
22754     /**
22755      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22756      * if the drop point is valid for over/enter..
22757      */
22758     valid : false,
22759     // private
22760     isTarget : true,
22761
22762     // private
22763     isNotifyTarget : true,
22764     
22765     /**
22766      * @hide
22767      */
22768     notifyEnter : function(dd, e, data)
22769     {
22770         this.valid = true;
22771         this.fireEvent('enter', dd, e, data);
22772         if(this.overClass){
22773             this.el.addClass(this.overClass);
22774         }
22775         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22776             this.valid ? this.dropAllowed : this.dropNotAllowed
22777         );
22778     },
22779
22780     /**
22781      * @hide
22782      */
22783     notifyOver : function(dd, e, data)
22784     {
22785         this.valid = true;
22786         this.fireEvent('over', dd, e, data);
22787         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22788             this.valid ? this.dropAllowed : this.dropNotAllowed
22789         );
22790     },
22791
22792     /**
22793      * @hide
22794      */
22795     notifyOut : function(dd, e, data)
22796     {
22797         this.fireEvent('out', dd, e, data);
22798         if(this.overClass){
22799             this.el.removeClass(this.overClass);
22800         }
22801     },
22802
22803     /**
22804      * @hide
22805      */
22806     notifyDrop : function(dd, e, data)
22807     {
22808         this.success = false;
22809         this.fireEvent('drop', dd, e, data);
22810         return this.success;
22811     }
22812 });/*
22813  * Based on:
22814  * Ext JS Library 1.1.1
22815  * Copyright(c) 2006-2007, Ext JS, LLC.
22816  *
22817  * Originally Released Under LGPL - original licence link has changed is not relivant.
22818  *
22819  * Fork - LGPL
22820  * <script type="text/javascript">
22821  */
22822
22823
22824 /**
22825  * @class Roo.dd.DragZone
22826  * @extends Roo.dd.DragSource
22827  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22828  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22829  * @constructor
22830  * @param {String/HTMLElement/Element} el The container element
22831  * @param {Object} config
22832  */
22833 Roo.dd.DragZone = function(el, config){
22834     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22835     if(this.containerScroll){
22836         Roo.dd.ScrollManager.register(this.el);
22837     }
22838 };
22839
22840 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22841     /**
22842      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22843      * for auto scrolling during drag operations.
22844      */
22845     /**
22846      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22847      * method after a failed drop (defaults to "c3daf9" - light blue)
22848      */
22849
22850     /**
22851      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22852      * for a valid target to drag based on the mouse down. Override this method
22853      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22854      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22855      * @param {EventObject} e The mouse down event
22856      * @return {Object} The dragData
22857      */
22858     getDragData : function(e){
22859         return Roo.dd.Registry.getHandleFromEvent(e);
22860     },
22861     
22862     /**
22863      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22864      * this.dragData.ddel
22865      * @param {Number} x The x position of the click on the dragged object
22866      * @param {Number} y The y position of the click on the dragged object
22867      * @return {Boolean} true to continue the drag, false to cancel
22868      */
22869     onInitDrag : function(x, y){
22870         this.proxy.update(this.dragData.ddel.cloneNode(true));
22871         this.onStartDrag(x, y);
22872         return true;
22873     },
22874     
22875     /**
22876      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22877      */
22878     afterRepair : function(){
22879         if(Roo.enableFx){
22880             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22881         }
22882         this.dragging = false;
22883     },
22884
22885     /**
22886      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22887      * the XY of this.dragData.ddel
22888      * @param {EventObject} e The mouse up event
22889      * @return {Array} The xy location (e.g. [100, 200])
22890      */
22891     getRepairXY : function(e){
22892         return Roo.Element.fly(this.dragData.ddel).getXY();  
22893     }
22894 });/*
22895  * Based on:
22896  * Ext JS Library 1.1.1
22897  * Copyright(c) 2006-2007, Ext JS, LLC.
22898  *
22899  * Originally Released Under LGPL - original licence link has changed is not relivant.
22900  *
22901  * Fork - LGPL
22902  * <script type="text/javascript">
22903  */
22904 /**
22905  * @class Roo.dd.DropZone
22906  * @extends Roo.dd.DropTarget
22907  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22908  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22909  * @constructor
22910  * @param {String/HTMLElement/Element} el The container element
22911  * @param {Object} config
22912  */
22913 Roo.dd.DropZone = function(el, config){
22914     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22915 };
22916
22917 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22918     /**
22919      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22920      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22921      * provide your own custom lookup.
22922      * @param {Event} e The event
22923      * @return {Object} data The custom data
22924      */
22925     getTargetFromEvent : function(e){
22926         return Roo.dd.Registry.getTargetFromEvent(e);
22927     },
22928
22929     /**
22930      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22931      * that it has registered.  This method has no default implementation and should be overridden to provide
22932      * node-specific processing if necessary.
22933      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22934      * {@link #getTargetFromEvent} for this node)
22935      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22936      * @param {Event} e The event
22937      * @param {Object} data An object containing arbitrary data supplied by the drag source
22938      */
22939     onNodeEnter : function(n, dd, e, data){
22940         
22941     },
22942
22943     /**
22944      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
22945      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
22946      * overridden to provide the proper feedback.
22947      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22948      * {@link #getTargetFromEvent} for this node)
22949      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22950      * @param {Event} e The event
22951      * @param {Object} data An object containing arbitrary data supplied by the drag source
22952      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22953      * underlying {@link Roo.dd.StatusProxy} can be updated
22954      */
22955     onNodeOver : function(n, dd, e, data){
22956         return this.dropAllowed;
22957     },
22958
22959     /**
22960      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
22961      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
22962      * node-specific processing if necessary.
22963      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22964      * {@link #getTargetFromEvent} for this node)
22965      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22966      * @param {Event} e The event
22967      * @param {Object} data An object containing arbitrary data supplied by the drag source
22968      */
22969     onNodeOut : function(n, dd, e, data){
22970         
22971     },
22972
22973     /**
22974      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
22975      * the drop node.  The default implementation returns false, so it should be overridden to provide the
22976      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
22977      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
22978      * {@link #getTargetFromEvent} for this node)
22979      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22980      * @param {Event} e The event
22981      * @param {Object} data An object containing arbitrary data supplied by the drag source
22982      * @return {Boolean} True if the drop was valid, else false
22983      */
22984     onNodeDrop : function(n, dd, e, data){
22985         return false;
22986     },
22987
22988     /**
22989      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
22990      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
22991      * it should be overridden to provide the proper feedback if necessary.
22992      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22993      * @param {Event} e The event
22994      * @param {Object} data An object containing arbitrary data supplied by the drag source
22995      * @return {String} status The CSS class that communicates the drop status back to the source so that the
22996      * underlying {@link Roo.dd.StatusProxy} can be updated
22997      */
22998     onContainerOver : function(dd, e, data){
22999         return this.dropNotAllowed;
23000     },
23001
23002     /**
23003      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23004      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23005      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23006      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23007      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23008      * @param {Event} e The event
23009      * @param {Object} data An object containing arbitrary data supplied by the drag source
23010      * @return {Boolean} True if the drop was valid, else false
23011      */
23012     onContainerDrop : function(dd, e, data){
23013         return false;
23014     },
23015
23016     /**
23017      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23018      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23019      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23020      * you should override this method and provide a custom implementation.
23021      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23022      * @param {Event} e The event
23023      * @param {Object} data An object containing arbitrary data supplied by the drag source
23024      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23025      * underlying {@link Roo.dd.StatusProxy} can be updated
23026      */
23027     notifyEnter : function(dd, e, data){
23028         return this.dropNotAllowed;
23029     },
23030
23031     /**
23032      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23033      * This method will be called on every mouse movement while the drag source is over the drop zone.
23034      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23035      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23036      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23037      * registered node, it will call {@link #onContainerOver}.
23038      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23039      * @param {Event} e The event
23040      * @param {Object} data An object containing arbitrary data supplied by the drag source
23041      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23042      * underlying {@link Roo.dd.StatusProxy} can be updated
23043      */
23044     notifyOver : function(dd, e, data){
23045         var n = this.getTargetFromEvent(e);
23046         if(!n){ // not over valid drop target
23047             if(this.lastOverNode){
23048                 this.onNodeOut(this.lastOverNode, dd, e, data);
23049                 this.lastOverNode = null;
23050             }
23051             return this.onContainerOver(dd, e, data);
23052         }
23053         if(this.lastOverNode != n){
23054             if(this.lastOverNode){
23055                 this.onNodeOut(this.lastOverNode, dd, e, data);
23056             }
23057             this.onNodeEnter(n, dd, e, data);
23058             this.lastOverNode = n;
23059         }
23060         return this.onNodeOver(n, dd, e, data);
23061     },
23062
23063     /**
23064      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23065      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23066      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23067      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23068      * @param {Event} e The event
23069      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23070      */
23071     notifyOut : function(dd, e, data){
23072         if(this.lastOverNode){
23073             this.onNodeOut(this.lastOverNode, dd, e, data);
23074             this.lastOverNode = null;
23075         }
23076     },
23077
23078     /**
23079      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23080      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23081      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23082      * otherwise it will call {@link #onContainerDrop}.
23083      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23084      * @param {Event} e The event
23085      * @param {Object} data An object containing arbitrary data supplied by the drag source
23086      * @return {Boolean} True if the drop was valid, else false
23087      */
23088     notifyDrop : function(dd, e, data){
23089         if(this.lastOverNode){
23090             this.onNodeOut(this.lastOverNode, dd, e, data);
23091             this.lastOverNode = null;
23092         }
23093         var n = this.getTargetFromEvent(e);
23094         return n ?
23095             this.onNodeDrop(n, dd, e, data) :
23096             this.onContainerDrop(dd, e, data);
23097     },
23098
23099     // private
23100     triggerCacheRefresh : function(){
23101         Roo.dd.DDM.refreshCache(this.groups);
23102     }  
23103 });/*
23104  * Based on:
23105  * Ext JS Library 1.1.1
23106  * Copyright(c) 2006-2007, Ext JS, LLC.
23107  *
23108  * Originally Released Under LGPL - original licence link has changed is not relivant.
23109  *
23110  * Fork - LGPL
23111  * <script type="text/javascript">
23112  */
23113
23114
23115 /**
23116  * @class Roo.data.SortTypes
23117  * @singleton
23118  * Defines the default sorting (casting?) comparison functions used when sorting data.
23119  */
23120 Roo.data.SortTypes = {
23121     /**
23122      * Default sort that does nothing
23123      * @param {Mixed} s The value being converted
23124      * @return {Mixed} The comparison value
23125      */
23126     none : function(s){
23127         return s;
23128     },
23129     
23130     /**
23131      * The regular expression used to strip tags
23132      * @type {RegExp}
23133      * @property
23134      */
23135     stripTagsRE : /<\/?[^>]+>/gi,
23136     
23137     /**
23138      * Strips all HTML tags to sort on text only
23139      * @param {Mixed} s The value being converted
23140      * @return {String} The comparison value
23141      */
23142     asText : function(s){
23143         return String(s).replace(this.stripTagsRE, "");
23144     },
23145     
23146     /**
23147      * Strips all HTML tags to sort on text only - Case insensitive
23148      * @param {Mixed} s The value being converted
23149      * @return {String} The comparison value
23150      */
23151     asUCText : function(s){
23152         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23153     },
23154     
23155     /**
23156      * Case insensitive string
23157      * @param {Mixed} s The value being converted
23158      * @return {String} The comparison value
23159      */
23160     asUCString : function(s) {
23161         return String(s).toUpperCase();
23162     },
23163     
23164     /**
23165      * Date sorting
23166      * @param {Mixed} s The value being converted
23167      * @return {Number} The comparison value
23168      */
23169     asDate : function(s) {
23170         if(!s){
23171             return 0;
23172         }
23173         if(s instanceof Date){
23174             return s.getTime();
23175         }
23176         return Date.parse(String(s));
23177     },
23178     
23179     /**
23180      * Float sorting
23181      * @param {Mixed} s The value being converted
23182      * @return {Float} The comparison value
23183      */
23184     asFloat : function(s) {
23185         var val = parseFloat(String(s).replace(/,/g, ""));
23186         if(isNaN(val)) {
23187             val = 0;
23188         }
23189         return val;
23190     },
23191     
23192     /**
23193      * Integer sorting
23194      * @param {Mixed} s The value being converted
23195      * @return {Number} The comparison value
23196      */
23197     asInt : function(s) {
23198         var val = parseInt(String(s).replace(/,/g, ""));
23199         if(isNaN(val)) {
23200             val = 0;
23201         }
23202         return val;
23203     }
23204 };/*
23205  * Based on:
23206  * Ext JS Library 1.1.1
23207  * Copyright(c) 2006-2007, Ext JS, LLC.
23208  *
23209  * Originally Released Under LGPL - original licence link has changed is not relivant.
23210  *
23211  * Fork - LGPL
23212  * <script type="text/javascript">
23213  */
23214
23215 /**
23216 * @class Roo.data.Record
23217  * Instances of this class encapsulate both record <em>definition</em> information, and record
23218  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23219  * to access Records cached in an {@link Roo.data.Store} object.<br>
23220  * <p>
23221  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23222  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23223  * objects.<br>
23224  * <p>
23225  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23226  * @constructor
23227  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23228  * {@link #create}. The parameters are the same.
23229  * @param {Array} data An associative Array of data values keyed by the field name.
23230  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23231  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23232  * not specified an integer id is generated.
23233  */
23234 Roo.data.Record = function(data, id){
23235     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23236     this.data = data;
23237 };
23238
23239 /**
23240  * Generate a constructor for a specific record layout.
23241  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23242  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23243  * Each field definition object may contain the following properties: <ul>
23244  * <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,
23245  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23246  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23247  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23248  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23249  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23250  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23251  * this may be omitted.</p></li>
23252  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23253  * <ul><li>auto (Default, implies no conversion)</li>
23254  * <li>string</li>
23255  * <li>int</li>
23256  * <li>float</li>
23257  * <li>boolean</li>
23258  * <li>date</li></ul></p></li>
23259  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23260  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23261  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23262  * by the Reader into an object that will be stored in the Record. It is passed the
23263  * following parameters:<ul>
23264  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23265  * </ul></p></li>
23266  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23267  * </ul>
23268  * <br>usage:<br><pre><code>
23269 var TopicRecord = Roo.data.Record.create(
23270     {name: 'title', mapping: 'topic_title'},
23271     {name: 'author', mapping: 'username'},
23272     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23273     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23274     {name: 'lastPoster', mapping: 'user2'},
23275     {name: 'excerpt', mapping: 'post_text'}
23276 );
23277
23278 var myNewRecord = new TopicRecord({
23279     title: 'Do my job please',
23280     author: 'noobie',
23281     totalPosts: 1,
23282     lastPost: new Date(),
23283     lastPoster: 'Animal',
23284     excerpt: 'No way dude!'
23285 });
23286 myStore.add(myNewRecord);
23287 </code></pre>
23288  * @method create
23289  * @static
23290  */
23291 Roo.data.Record.create = function(o){
23292     var f = function(){
23293         f.superclass.constructor.apply(this, arguments);
23294     };
23295     Roo.extend(f, Roo.data.Record);
23296     var p = f.prototype;
23297     p.fields = new Roo.util.MixedCollection(false, function(field){
23298         return field.name;
23299     });
23300     for(var i = 0, len = o.length; i < len; i++){
23301         p.fields.add(new Roo.data.Field(o[i]));
23302     }
23303     f.getField = function(name){
23304         return p.fields.get(name);  
23305     };
23306     return f;
23307 };
23308
23309 Roo.data.Record.AUTO_ID = 1000;
23310 Roo.data.Record.EDIT = 'edit';
23311 Roo.data.Record.REJECT = 'reject';
23312 Roo.data.Record.COMMIT = 'commit';
23313
23314 Roo.data.Record.prototype = {
23315     /**
23316      * Readonly flag - true if this record has been modified.
23317      * @type Boolean
23318      */
23319     dirty : false,
23320     editing : false,
23321     error: null,
23322     modified: null,
23323
23324     // private
23325     join : function(store){
23326         this.store = store;
23327     },
23328
23329     /**
23330      * Set the named field to the specified value.
23331      * @param {String} name The name of the field to set.
23332      * @param {Object} value The value to set the field to.
23333      */
23334     set : function(name, value){
23335         if(this.data[name] == value){
23336             return;
23337         }
23338         this.dirty = true;
23339         if(!this.modified){
23340             this.modified = {};
23341         }
23342         if(typeof this.modified[name] == 'undefined'){
23343             this.modified[name] = this.data[name];
23344         }
23345         this.data[name] = value;
23346         if(!this.editing && this.store){
23347             this.store.afterEdit(this);
23348         }       
23349     },
23350
23351     /**
23352      * Get the value of the named field.
23353      * @param {String} name The name of the field to get the value of.
23354      * @return {Object} The value of the field.
23355      */
23356     get : function(name){
23357         return this.data[name]; 
23358     },
23359
23360     // private
23361     beginEdit : function(){
23362         this.editing = true;
23363         this.modified = {}; 
23364     },
23365
23366     // private
23367     cancelEdit : function(){
23368         this.editing = false;
23369         delete this.modified;
23370     },
23371
23372     // private
23373     endEdit : function(){
23374         this.editing = false;
23375         if(this.dirty && this.store){
23376             this.store.afterEdit(this);
23377         }
23378     },
23379
23380     /**
23381      * Usually called by the {@link Roo.data.Store} which owns the Record.
23382      * Rejects all changes made to the Record since either creation, or the last commit operation.
23383      * Modified fields are reverted to their original values.
23384      * <p>
23385      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23386      * of reject operations.
23387      */
23388     reject : function(){
23389         var m = this.modified;
23390         for(var n in m){
23391             if(typeof m[n] != "function"){
23392                 this.data[n] = m[n];
23393             }
23394         }
23395         this.dirty = false;
23396         delete this.modified;
23397         this.editing = false;
23398         if(this.store){
23399             this.store.afterReject(this);
23400         }
23401     },
23402
23403     /**
23404      * Usually called by the {@link Roo.data.Store} which owns the Record.
23405      * Commits all changes made to the Record since either creation, or the last commit operation.
23406      * <p>
23407      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23408      * of commit operations.
23409      */
23410     commit : function(){
23411         this.dirty = false;
23412         delete this.modified;
23413         this.editing = false;
23414         if(this.store){
23415             this.store.afterCommit(this);
23416         }
23417     },
23418
23419     // private
23420     hasError : function(){
23421         return this.error != null;
23422     },
23423
23424     // private
23425     clearError : function(){
23426         this.error = null;
23427     },
23428
23429     /**
23430      * Creates a copy of this record.
23431      * @param {String} id (optional) A new record id if you don't want to use this record's id
23432      * @return {Record}
23433      */
23434     copy : function(newId) {
23435         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23436     }
23437 };/*
23438  * Based on:
23439  * Ext JS Library 1.1.1
23440  * Copyright(c) 2006-2007, Ext JS, LLC.
23441  *
23442  * Originally Released Under LGPL - original licence link has changed is not relivant.
23443  *
23444  * Fork - LGPL
23445  * <script type="text/javascript">
23446  */
23447
23448
23449
23450 /**
23451  * @class Roo.data.Store
23452  * @extends Roo.util.Observable
23453  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23454  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23455  * <p>
23456  * 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
23457  * has no knowledge of the format of the data returned by the Proxy.<br>
23458  * <p>
23459  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23460  * instances from the data object. These records are cached and made available through accessor functions.
23461  * @constructor
23462  * Creates a new Store.
23463  * @param {Object} config A config object containing the objects needed for the Store to access data,
23464  * and read the data into Records.
23465  */
23466 Roo.data.Store = function(config){
23467     this.data = new Roo.util.MixedCollection(false);
23468     this.data.getKey = function(o){
23469         return o.id;
23470     };
23471     this.baseParams = {};
23472     // private
23473     this.paramNames = {
23474         "start" : "start",
23475         "limit" : "limit",
23476         "sort" : "sort",
23477         "dir" : "dir",
23478         "multisort" : "_multisort"
23479     };
23480
23481     if(config && config.data){
23482         this.inlineData = config.data;
23483         delete config.data;
23484     }
23485
23486     Roo.apply(this, config);
23487     
23488     if(this.reader){ // reader passed
23489         this.reader = Roo.factory(this.reader, Roo.data);
23490         this.reader.xmodule = this.xmodule || false;
23491         if(!this.recordType){
23492             this.recordType = this.reader.recordType;
23493         }
23494         if(this.reader.onMetaChange){
23495             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23496         }
23497     }
23498
23499     if(this.recordType){
23500         this.fields = this.recordType.prototype.fields;
23501     }
23502     this.modified = [];
23503
23504     this.addEvents({
23505         /**
23506          * @event datachanged
23507          * Fires when the data cache has changed, and a widget which is using this Store
23508          * as a Record cache should refresh its view.
23509          * @param {Store} this
23510          */
23511         datachanged : true,
23512         /**
23513          * @event metachange
23514          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23515          * @param {Store} this
23516          * @param {Object} meta The JSON metadata
23517          */
23518         metachange : true,
23519         /**
23520          * @event add
23521          * Fires when Records have been added to the Store
23522          * @param {Store} this
23523          * @param {Roo.data.Record[]} records The array of Records added
23524          * @param {Number} index The index at which the record(s) were added
23525          */
23526         add : true,
23527         /**
23528          * @event remove
23529          * Fires when a Record has been removed from the Store
23530          * @param {Store} this
23531          * @param {Roo.data.Record} record The Record that was removed
23532          * @param {Number} index The index at which the record was removed
23533          */
23534         remove : true,
23535         /**
23536          * @event update
23537          * Fires when a Record has been updated
23538          * @param {Store} this
23539          * @param {Roo.data.Record} record The Record that was updated
23540          * @param {String} operation The update operation being performed.  Value may be one of:
23541          * <pre><code>
23542  Roo.data.Record.EDIT
23543  Roo.data.Record.REJECT
23544  Roo.data.Record.COMMIT
23545          * </code></pre>
23546          */
23547         update : true,
23548         /**
23549          * @event clear
23550          * Fires when the data cache has been cleared.
23551          * @param {Store} this
23552          */
23553         clear : true,
23554         /**
23555          * @event beforeload
23556          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23557          * the load action will be canceled.
23558          * @param {Store} this
23559          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23560          */
23561         beforeload : true,
23562         /**
23563          * @event beforeloadadd
23564          * Fires after a new set of Records has been loaded.
23565          * @param {Store} this
23566          * @param {Roo.data.Record[]} records The Records that were loaded
23567          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23568          */
23569         beforeloadadd : true,
23570         /**
23571          * @event load
23572          * Fires after a new set of Records has been loaded, before they are added to the store.
23573          * @param {Store} this
23574          * @param {Roo.data.Record[]} records The Records that were loaded
23575          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23576          * @params {Object} return from reader
23577          */
23578         load : true,
23579         /**
23580          * @event loadexception
23581          * Fires if an exception occurs in the Proxy during loading.
23582          * Called with the signature of the Proxy's "loadexception" event.
23583          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23584          * 
23585          * @param {Proxy} 
23586          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23587          * @param {Object} load options 
23588          * @param {Object} jsonData from your request (normally this contains the Exception)
23589          */
23590         loadexception : true
23591     });
23592     
23593     if(this.proxy){
23594         this.proxy = Roo.factory(this.proxy, Roo.data);
23595         this.proxy.xmodule = this.xmodule || false;
23596         this.relayEvents(this.proxy,  ["loadexception"]);
23597     }
23598     this.sortToggle = {};
23599     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23600
23601     Roo.data.Store.superclass.constructor.call(this);
23602
23603     if(this.inlineData){
23604         this.loadData(this.inlineData);
23605         delete this.inlineData;
23606     }
23607 };
23608
23609 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23610      /**
23611     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23612     * without a remote query - used by combo/forms at present.
23613     */
23614     
23615     /**
23616     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23617     */
23618     /**
23619     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23620     */
23621     /**
23622     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23623     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23624     */
23625     /**
23626     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23627     * on any HTTP request
23628     */
23629     /**
23630     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23631     */
23632     /**
23633     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23634     */
23635     multiSort: false,
23636     /**
23637     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23638     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23639     */
23640     remoteSort : false,
23641
23642     /**
23643     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23644      * loaded or when a record is removed. (defaults to false).
23645     */
23646     pruneModifiedRecords : false,
23647
23648     // private
23649     lastOptions : null,
23650
23651     /**
23652      * Add Records to the Store and fires the add event.
23653      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23654      */
23655     add : function(records){
23656         records = [].concat(records);
23657         for(var i = 0, len = records.length; i < len; i++){
23658             records[i].join(this);
23659         }
23660         var index = this.data.length;
23661         this.data.addAll(records);
23662         this.fireEvent("add", this, records, index);
23663     },
23664
23665     /**
23666      * Remove a Record from the Store and fires the remove event.
23667      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23668      */
23669     remove : function(record){
23670         var index = this.data.indexOf(record);
23671         this.data.removeAt(index);
23672  
23673         if(this.pruneModifiedRecords){
23674             this.modified.remove(record);
23675         }
23676         this.fireEvent("remove", this, record, index);
23677     },
23678
23679     /**
23680      * Remove all Records from the Store and fires the clear event.
23681      */
23682     removeAll : function(){
23683         this.data.clear();
23684         if(this.pruneModifiedRecords){
23685             this.modified = [];
23686         }
23687         this.fireEvent("clear", this);
23688     },
23689
23690     /**
23691      * Inserts Records to the Store at the given index and fires the add event.
23692      * @param {Number} index The start index at which to insert the passed Records.
23693      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23694      */
23695     insert : function(index, records){
23696         records = [].concat(records);
23697         for(var i = 0, len = records.length; i < len; i++){
23698             this.data.insert(index, records[i]);
23699             records[i].join(this);
23700         }
23701         this.fireEvent("add", this, records, index);
23702     },
23703
23704     /**
23705      * Get the index within the cache of the passed Record.
23706      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23707      * @return {Number} The index of the passed Record. Returns -1 if not found.
23708      */
23709     indexOf : function(record){
23710         return this.data.indexOf(record);
23711     },
23712
23713     /**
23714      * Get the index within the cache of the Record with the passed id.
23715      * @param {String} id The id of the Record to find.
23716      * @return {Number} The index of the Record. Returns -1 if not found.
23717      */
23718     indexOfId : function(id){
23719         return this.data.indexOfKey(id);
23720     },
23721
23722     /**
23723      * Get the Record with the specified id.
23724      * @param {String} id The id of the Record to find.
23725      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23726      */
23727     getById : function(id){
23728         return this.data.key(id);
23729     },
23730
23731     /**
23732      * Get the Record at the specified index.
23733      * @param {Number} index The index of the Record to find.
23734      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23735      */
23736     getAt : function(index){
23737         return this.data.itemAt(index);
23738     },
23739
23740     /**
23741      * Returns a range of Records between specified indices.
23742      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23743      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23744      * @return {Roo.data.Record[]} An array of Records
23745      */
23746     getRange : function(start, end){
23747         return this.data.getRange(start, end);
23748     },
23749
23750     // private
23751     storeOptions : function(o){
23752         o = Roo.apply({}, o);
23753         delete o.callback;
23754         delete o.scope;
23755         this.lastOptions = o;
23756     },
23757
23758     /**
23759      * Loads the Record cache from the configured Proxy using the configured Reader.
23760      * <p>
23761      * If using remote paging, then the first load call must specify the <em>start</em>
23762      * and <em>limit</em> properties in the options.params property to establish the initial
23763      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23764      * <p>
23765      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23766      * and this call will return before the new data has been loaded. Perform any post-processing
23767      * in a callback function, or in a "load" event handler.</strong>
23768      * <p>
23769      * @param {Object} options An object containing properties which control loading options:<ul>
23770      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23771      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23772      * passed the following arguments:<ul>
23773      * <li>r : Roo.data.Record[]</li>
23774      * <li>options: Options object from the load call</li>
23775      * <li>success: Boolean success indicator</li></ul></li>
23776      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23777      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23778      * </ul>
23779      */
23780     load : function(options){
23781         options = options || {};
23782         if(this.fireEvent("beforeload", this, options) !== false){
23783             this.storeOptions(options);
23784             var p = Roo.apply(options.params || {}, this.baseParams);
23785             // if meta was not loaded from remote source.. try requesting it.
23786             if (!this.reader.metaFromRemote) {
23787                 p._requestMeta = 1;
23788             }
23789             if(this.sortInfo && this.remoteSort){
23790                 var pn = this.paramNames;
23791                 p[pn["sort"]] = this.sortInfo.field;
23792                 p[pn["dir"]] = this.sortInfo.direction;
23793             }
23794             if (this.multiSort) {
23795                 var pn = this.paramNames;
23796                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23797             }
23798             
23799             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23800         }
23801     },
23802
23803     /**
23804      * Reloads the Record cache from the configured Proxy using the configured Reader and
23805      * the options from the last load operation performed.
23806      * @param {Object} options (optional) An object containing properties which may override the options
23807      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23808      * the most recently used options are reused).
23809      */
23810     reload : function(options){
23811         this.load(Roo.applyIf(options||{}, this.lastOptions));
23812     },
23813
23814     // private
23815     // Called as a callback by the Reader during a load operation.
23816     loadRecords : function(o, options, success){
23817         if(!o || success === false){
23818             if(success !== false){
23819                 this.fireEvent("load", this, [], options, o);
23820             }
23821             if(options.callback){
23822                 options.callback.call(options.scope || this, [], options, false);
23823             }
23824             return;
23825         }
23826         // if data returned failure - throw an exception.
23827         if (o.success === false) {
23828             // show a message if no listener is registered.
23829             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23830                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23831             }
23832             // loadmask wil be hooked into this..
23833             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23834             return;
23835         }
23836         var r = o.records, t = o.totalRecords || r.length;
23837         
23838         this.fireEvent("beforeloadadd", this, r, options, o);
23839         
23840         if(!options || options.add !== true){
23841             if(this.pruneModifiedRecords){
23842                 this.modified = [];
23843             }
23844             for(var i = 0, len = r.length; i < len; i++){
23845                 r[i].join(this);
23846             }
23847             if(this.snapshot){
23848                 this.data = this.snapshot;
23849                 delete this.snapshot;
23850             }
23851             this.data.clear();
23852             this.data.addAll(r);
23853             this.totalLength = t;
23854             this.applySort();
23855             this.fireEvent("datachanged", this);
23856         }else{
23857             this.totalLength = Math.max(t, this.data.length+r.length);
23858             this.add(r);
23859         }
23860         
23861         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23862                 
23863             var e = new Roo.data.Record({});
23864
23865             e.set(this.parent.displayField, this.parent.emptyTitle);
23866             e.set(this.parent.valueField, '');
23867
23868             this.insert(0, e);
23869         }
23870             
23871         this.fireEvent("load", this, r, options, o);
23872         if(options.callback){
23873             options.callback.call(options.scope || this, r, options, true);
23874         }
23875     },
23876
23877
23878     /**
23879      * Loads data from a passed data block. A Reader which understands the format of the data
23880      * must have been configured in the constructor.
23881      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23882      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23883      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23884      */
23885     loadData : function(o, append){
23886         var r = this.reader.readRecords(o);
23887         this.loadRecords(r, {add: append}, true);
23888     },
23889     
23890      /**
23891      * using 'cn' the nested child reader read the child array into it's child stores.
23892      * @param {Object} rec The record with a 'children array
23893      */
23894     loadDataFromChildren : function(rec)
23895     {
23896         this.loadData(this.reader.toLoadData(rec));
23897     },
23898     
23899
23900     /**
23901      * Gets the number of cached records.
23902      * <p>
23903      * <em>If using paging, this may not be the total size of the dataset. If the data object
23904      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23905      * the data set size</em>
23906      */
23907     getCount : function(){
23908         return this.data.length || 0;
23909     },
23910
23911     /**
23912      * Gets the total number of records in the dataset as returned by the server.
23913      * <p>
23914      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23915      * the dataset size</em>
23916      */
23917     getTotalCount : function(){
23918         return this.totalLength || 0;
23919     },
23920
23921     /**
23922      * Returns the sort state of the Store as an object with two properties:
23923      * <pre><code>
23924  field {String} The name of the field by which the Records are sorted
23925  direction {String} The sort order, "ASC" or "DESC"
23926      * </code></pre>
23927      */
23928     getSortState : function(){
23929         return this.sortInfo;
23930     },
23931
23932     // private
23933     applySort : function(){
23934         if(this.sortInfo && !this.remoteSort){
23935             var s = this.sortInfo, f = s.field;
23936             var st = this.fields.get(f).sortType;
23937             var fn = function(r1, r2){
23938                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23939                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
23940             };
23941             this.data.sort(s.direction, fn);
23942             if(this.snapshot && this.snapshot != this.data){
23943                 this.snapshot.sort(s.direction, fn);
23944             }
23945         }
23946     },
23947
23948     /**
23949      * Sets the default sort column and order to be used by the next load operation.
23950      * @param {String} fieldName The name of the field to sort by.
23951      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23952      */
23953     setDefaultSort : function(field, dir){
23954         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
23955     },
23956
23957     /**
23958      * Sort the Records.
23959      * If remote sorting is used, the sort is performed on the server, and the cache is
23960      * reloaded. If local sorting is used, the cache is sorted internally.
23961      * @param {String} fieldName The name of the field to sort by.
23962      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
23963      */
23964     sort : function(fieldName, dir){
23965         var f = this.fields.get(fieldName);
23966         if(!dir){
23967             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
23968             
23969             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
23970                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
23971             }else{
23972                 dir = f.sortDir;
23973             }
23974         }
23975         this.sortToggle[f.name] = dir;
23976         this.sortInfo = {field: f.name, direction: dir};
23977         if(!this.remoteSort){
23978             this.applySort();
23979             this.fireEvent("datachanged", this);
23980         }else{
23981             this.load(this.lastOptions);
23982         }
23983     },
23984
23985     /**
23986      * Calls the specified function for each of the Records in the cache.
23987      * @param {Function} fn The function to call. The Record is passed as the first parameter.
23988      * Returning <em>false</em> aborts and exits the iteration.
23989      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
23990      */
23991     each : function(fn, scope){
23992         this.data.each(fn, scope);
23993     },
23994
23995     /**
23996      * Gets all records modified since the last commit.  Modified records are persisted across load operations
23997      * (e.g., during paging).
23998      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
23999      */
24000     getModifiedRecords : function(){
24001         return this.modified;
24002     },
24003
24004     // private
24005     createFilterFn : function(property, value, anyMatch){
24006         if(!value.exec){ // not a regex
24007             value = String(value);
24008             if(value.length == 0){
24009                 return false;
24010             }
24011             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24012         }
24013         return function(r){
24014             return value.test(r.data[property]);
24015         };
24016     },
24017
24018     /**
24019      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24020      * @param {String} property A field on your records
24021      * @param {Number} start The record index to start at (defaults to 0)
24022      * @param {Number} end The last record index to include (defaults to length - 1)
24023      * @return {Number} The sum
24024      */
24025     sum : function(property, start, end){
24026         var rs = this.data.items, v = 0;
24027         start = start || 0;
24028         end = (end || end === 0) ? end : rs.length-1;
24029
24030         for(var i = start; i <= end; i++){
24031             v += (rs[i].data[property] || 0);
24032         }
24033         return v;
24034     },
24035
24036     /**
24037      * Filter the records by a specified property.
24038      * @param {String} field A field on your records
24039      * @param {String/RegExp} value Either a string that the field
24040      * should start with or a RegExp to test against the field
24041      * @param {Boolean} anyMatch True to match any part not just the beginning
24042      */
24043     filter : function(property, value, anyMatch){
24044         var fn = this.createFilterFn(property, value, anyMatch);
24045         return fn ? this.filterBy(fn) : this.clearFilter();
24046     },
24047
24048     /**
24049      * Filter by a function. The specified function will be called with each
24050      * record in this data source. If the function returns true the record is included,
24051      * otherwise it is filtered.
24052      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24053      * @param {Object} scope (optional) The scope of the function (defaults to this)
24054      */
24055     filterBy : function(fn, scope){
24056         this.snapshot = this.snapshot || this.data;
24057         this.data = this.queryBy(fn, scope||this);
24058         this.fireEvent("datachanged", this);
24059     },
24060
24061     /**
24062      * Query the records by a specified property.
24063      * @param {String} field A field on your records
24064      * @param {String/RegExp} value Either a string that the field
24065      * should start with or a RegExp to test against the field
24066      * @param {Boolean} anyMatch True to match any part not just the beginning
24067      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24068      */
24069     query : function(property, value, anyMatch){
24070         var fn = this.createFilterFn(property, value, anyMatch);
24071         return fn ? this.queryBy(fn) : this.data.clone();
24072     },
24073
24074     /**
24075      * Query by a function. The specified function will be called with each
24076      * record in this data source. If the function returns true the record is included
24077      * in the results.
24078      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24079      * @param {Object} scope (optional) The scope of the function (defaults to this)
24080       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24081      **/
24082     queryBy : function(fn, scope){
24083         var data = this.snapshot || this.data;
24084         return data.filterBy(fn, scope||this);
24085     },
24086
24087     /**
24088      * Collects unique values for a particular dataIndex from this store.
24089      * @param {String} dataIndex The property to collect
24090      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24091      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24092      * @return {Array} An array of the unique values
24093      **/
24094     collect : function(dataIndex, allowNull, bypassFilter){
24095         var d = (bypassFilter === true && this.snapshot) ?
24096                 this.snapshot.items : this.data.items;
24097         var v, sv, r = [], l = {};
24098         for(var i = 0, len = d.length; i < len; i++){
24099             v = d[i].data[dataIndex];
24100             sv = String(v);
24101             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24102                 l[sv] = true;
24103                 r[r.length] = v;
24104             }
24105         }
24106         return r;
24107     },
24108
24109     /**
24110      * Revert to a view of the Record cache with no filtering applied.
24111      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24112      */
24113     clearFilter : function(suppressEvent){
24114         if(this.snapshot && this.snapshot != this.data){
24115             this.data = this.snapshot;
24116             delete this.snapshot;
24117             if(suppressEvent !== true){
24118                 this.fireEvent("datachanged", this);
24119             }
24120         }
24121     },
24122
24123     // private
24124     afterEdit : function(record){
24125         if(this.modified.indexOf(record) == -1){
24126             this.modified.push(record);
24127         }
24128         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24129     },
24130     
24131     // private
24132     afterReject : function(record){
24133         this.modified.remove(record);
24134         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24135     },
24136
24137     // private
24138     afterCommit : function(record){
24139         this.modified.remove(record);
24140         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24141     },
24142
24143     /**
24144      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24145      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24146      */
24147     commitChanges : function(){
24148         var m = this.modified.slice(0);
24149         this.modified = [];
24150         for(var i = 0, len = m.length; i < len; i++){
24151             m[i].commit();
24152         }
24153     },
24154
24155     /**
24156      * Cancel outstanding changes on all changed records.
24157      */
24158     rejectChanges : function(){
24159         var m = this.modified.slice(0);
24160         this.modified = [];
24161         for(var i = 0, len = m.length; i < len; i++){
24162             m[i].reject();
24163         }
24164     },
24165
24166     onMetaChange : function(meta, rtype, o){
24167         this.recordType = rtype;
24168         this.fields = rtype.prototype.fields;
24169         delete this.snapshot;
24170         this.sortInfo = meta.sortInfo || this.sortInfo;
24171         this.modified = [];
24172         this.fireEvent('metachange', this, this.reader.meta);
24173     },
24174     
24175     moveIndex : function(data, type)
24176     {
24177         var index = this.indexOf(data);
24178         
24179         var newIndex = index + type;
24180         
24181         this.remove(data);
24182         
24183         this.insert(newIndex, data);
24184         
24185     }
24186 });/*
24187  * Based on:
24188  * Ext JS Library 1.1.1
24189  * Copyright(c) 2006-2007, Ext JS, LLC.
24190  *
24191  * Originally Released Under LGPL - original licence link has changed is not relivant.
24192  *
24193  * Fork - LGPL
24194  * <script type="text/javascript">
24195  */
24196
24197 /**
24198  * @class Roo.data.SimpleStore
24199  * @extends Roo.data.Store
24200  * Small helper class to make creating Stores from Array data easier.
24201  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24202  * @cfg {Array} fields An array of field definition objects, or field name strings.
24203  * @cfg {Object} an existing reader (eg. copied from another store)
24204  * @cfg {Array} data The multi-dimensional array of data
24205  * @constructor
24206  * @param {Object} config
24207  */
24208 Roo.data.SimpleStore = function(config)
24209 {
24210     Roo.data.SimpleStore.superclass.constructor.call(this, {
24211         isLocal : true,
24212         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24213                 id: config.id
24214             },
24215             Roo.data.Record.create(config.fields)
24216         ),
24217         proxy : new Roo.data.MemoryProxy(config.data)
24218     });
24219     this.load();
24220 };
24221 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24222  * Based on:
24223  * Ext JS Library 1.1.1
24224  * Copyright(c) 2006-2007, Ext JS, LLC.
24225  *
24226  * Originally Released Under LGPL - original licence link has changed is not relivant.
24227  *
24228  * Fork - LGPL
24229  * <script type="text/javascript">
24230  */
24231
24232 /**
24233 /**
24234  * @extends Roo.data.Store
24235  * @class Roo.data.JsonStore
24236  * Small helper class to make creating Stores for JSON data easier. <br/>
24237 <pre><code>
24238 var store = new Roo.data.JsonStore({
24239     url: 'get-images.php',
24240     root: 'images',
24241     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24242 });
24243 </code></pre>
24244  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24245  * JsonReader and HttpProxy (unless inline data is provided).</b>
24246  * @cfg {Array} fields An array of field definition objects, or field name strings.
24247  * @constructor
24248  * @param {Object} config
24249  */
24250 Roo.data.JsonStore = function(c){
24251     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24252         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24253         reader: new Roo.data.JsonReader(c, c.fields)
24254     }));
24255 };
24256 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24257  * Based on:
24258  * Ext JS Library 1.1.1
24259  * Copyright(c) 2006-2007, Ext JS, LLC.
24260  *
24261  * Originally Released Under LGPL - original licence link has changed is not relivant.
24262  *
24263  * Fork - LGPL
24264  * <script type="text/javascript">
24265  */
24266
24267  
24268 Roo.data.Field = function(config){
24269     if(typeof config == "string"){
24270         config = {name: config};
24271     }
24272     Roo.apply(this, config);
24273     
24274     if(!this.type){
24275         this.type = "auto";
24276     }
24277     
24278     var st = Roo.data.SortTypes;
24279     // named sortTypes are supported, here we look them up
24280     if(typeof this.sortType == "string"){
24281         this.sortType = st[this.sortType];
24282     }
24283     
24284     // set default sortType for strings and dates
24285     if(!this.sortType){
24286         switch(this.type){
24287             case "string":
24288                 this.sortType = st.asUCString;
24289                 break;
24290             case "date":
24291                 this.sortType = st.asDate;
24292                 break;
24293             default:
24294                 this.sortType = st.none;
24295         }
24296     }
24297
24298     // define once
24299     var stripRe = /[\$,%]/g;
24300
24301     // prebuilt conversion function for this field, instead of
24302     // switching every time we're reading a value
24303     if(!this.convert){
24304         var cv, dateFormat = this.dateFormat;
24305         switch(this.type){
24306             case "":
24307             case "auto":
24308             case undefined:
24309                 cv = function(v){ return v; };
24310                 break;
24311             case "string":
24312                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24313                 break;
24314             case "int":
24315                 cv = function(v){
24316                     return v !== undefined && v !== null && v !== '' ?
24317                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24318                     };
24319                 break;
24320             case "float":
24321                 cv = function(v){
24322                     return v !== undefined && v !== null && v !== '' ?
24323                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24324                     };
24325                 break;
24326             case "bool":
24327             case "boolean":
24328                 cv = function(v){ return v === true || v === "true" || v == 1; };
24329                 break;
24330             case "date":
24331                 cv = function(v){
24332                     if(!v){
24333                         return '';
24334                     }
24335                     if(v instanceof Date){
24336                         return v;
24337                     }
24338                     if(dateFormat){
24339                         if(dateFormat == "timestamp"){
24340                             return new Date(v*1000);
24341                         }
24342                         return Date.parseDate(v, dateFormat);
24343                     }
24344                     var parsed = Date.parse(v);
24345                     return parsed ? new Date(parsed) : null;
24346                 };
24347              break;
24348             
24349         }
24350         this.convert = cv;
24351     }
24352 };
24353
24354 Roo.data.Field.prototype = {
24355     dateFormat: null,
24356     defaultValue: "",
24357     mapping: null,
24358     sortType : null,
24359     sortDir : "ASC"
24360 };/*
24361  * Based on:
24362  * Ext JS Library 1.1.1
24363  * Copyright(c) 2006-2007, Ext JS, LLC.
24364  *
24365  * Originally Released Under LGPL - original licence link has changed is not relivant.
24366  *
24367  * Fork - LGPL
24368  * <script type="text/javascript">
24369  */
24370  
24371 // Base class for reading structured data from a data source.  This class is intended to be
24372 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24373
24374 /**
24375  * @class Roo.data.DataReader
24376  * Base class for reading structured data from a data source.  This class is intended to be
24377  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24378  */
24379
24380 Roo.data.DataReader = function(meta, recordType){
24381     
24382     this.meta = meta;
24383     
24384     this.recordType = recordType instanceof Array ? 
24385         Roo.data.Record.create(recordType) : recordType;
24386 };
24387
24388 Roo.data.DataReader.prototype = {
24389     
24390     
24391     readerType : 'Data',
24392      /**
24393      * Create an empty record
24394      * @param {Object} data (optional) - overlay some values
24395      * @return {Roo.data.Record} record created.
24396      */
24397     newRow :  function(d) {
24398         var da =  {};
24399         this.recordType.prototype.fields.each(function(c) {
24400             switch( c.type) {
24401                 case 'int' : da[c.name] = 0; break;
24402                 case 'date' : da[c.name] = new Date(); break;
24403                 case 'float' : da[c.name] = 0.0; break;
24404                 case 'boolean' : da[c.name] = false; break;
24405                 default : da[c.name] = ""; break;
24406             }
24407             
24408         });
24409         return new this.recordType(Roo.apply(da, d));
24410     }
24411     
24412     
24413 };/*
24414  * Based on:
24415  * Ext JS Library 1.1.1
24416  * Copyright(c) 2006-2007, Ext JS, LLC.
24417  *
24418  * Originally Released Under LGPL - original licence link has changed is not relivant.
24419  *
24420  * Fork - LGPL
24421  * <script type="text/javascript">
24422  */
24423
24424 /**
24425  * @class Roo.data.DataProxy
24426  * @extends Roo.data.Observable
24427  * This class is an abstract base class for implementations which provide retrieval of
24428  * unformatted data objects.<br>
24429  * <p>
24430  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24431  * (of the appropriate type which knows how to parse the data object) to provide a block of
24432  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24433  * <p>
24434  * Custom implementations must implement the load method as described in
24435  * {@link Roo.data.HttpProxy#load}.
24436  */
24437 Roo.data.DataProxy = function(){
24438     this.addEvents({
24439         /**
24440          * @event beforeload
24441          * Fires before a network request is made to retrieve a data object.
24442          * @param {Object} This DataProxy object.
24443          * @param {Object} params The params parameter to the load function.
24444          */
24445         beforeload : true,
24446         /**
24447          * @event load
24448          * Fires before the load method's callback is called.
24449          * @param {Object} This DataProxy object.
24450          * @param {Object} o The data object.
24451          * @param {Object} arg The callback argument object passed to the load function.
24452          */
24453         load : true,
24454         /**
24455          * @event loadexception
24456          * Fires if an Exception occurs during data retrieval.
24457          * @param {Object} This DataProxy object.
24458          * @param {Object} o The data object.
24459          * @param {Object} arg The callback argument object passed to the load function.
24460          * @param {Object} e The Exception.
24461          */
24462         loadexception : true
24463     });
24464     Roo.data.DataProxy.superclass.constructor.call(this);
24465 };
24466
24467 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24468
24469     /**
24470      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24471      */
24472 /*
24473  * Based on:
24474  * Ext JS Library 1.1.1
24475  * Copyright(c) 2006-2007, Ext JS, LLC.
24476  *
24477  * Originally Released Under LGPL - original licence link has changed is not relivant.
24478  *
24479  * Fork - LGPL
24480  * <script type="text/javascript">
24481  */
24482 /**
24483  * @class Roo.data.MemoryProxy
24484  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24485  * to the Reader when its load method is called.
24486  * @constructor
24487  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24488  */
24489 Roo.data.MemoryProxy = function(data){
24490     if (data.data) {
24491         data = data.data;
24492     }
24493     Roo.data.MemoryProxy.superclass.constructor.call(this);
24494     this.data = data;
24495 };
24496
24497 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24498     
24499     /**
24500      * Load data from the requested source (in this case an in-memory
24501      * data object passed to the constructor), read the data object into
24502      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24503      * process that block using the passed callback.
24504      * @param {Object} params This parameter is not used by the MemoryProxy class.
24505      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24506      * object into a block of Roo.data.Records.
24507      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24508      * The function must be passed <ul>
24509      * <li>The Record block object</li>
24510      * <li>The "arg" argument from the load function</li>
24511      * <li>A boolean success indicator</li>
24512      * </ul>
24513      * @param {Object} scope The scope in which to call the callback
24514      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24515      */
24516     load : function(params, reader, callback, scope, arg){
24517         params = params || {};
24518         var result;
24519         try {
24520             result = reader.readRecords(params.data ? params.data :this.data);
24521         }catch(e){
24522             this.fireEvent("loadexception", this, arg, null, e);
24523             callback.call(scope, null, arg, false);
24524             return;
24525         }
24526         callback.call(scope, result, arg, true);
24527     },
24528     
24529     // private
24530     update : function(params, records){
24531         
24532     }
24533 });/*
24534  * Based on:
24535  * Ext JS Library 1.1.1
24536  * Copyright(c) 2006-2007, Ext JS, LLC.
24537  *
24538  * Originally Released Under LGPL - original licence link has changed is not relivant.
24539  *
24540  * Fork - LGPL
24541  * <script type="text/javascript">
24542  */
24543 /**
24544  * @class Roo.data.HttpProxy
24545  * @extends Roo.data.DataProxy
24546  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24547  * configured to reference a certain URL.<br><br>
24548  * <p>
24549  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24550  * from which the running page was served.<br><br>
24551  * <p>
24552  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24553  * <p>
24554  * Be aware that to enable the browser to parse an XML document, the server must set
24555  * the Content-Type header in the HTTP response to "text/xml".
24556  * @constructor
24557  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24558  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24559  * will be used to make the request.
24560  */
24561 Roo.data.HttpProxy = function(conn){
24562     Roo.data.HttpProxy.superclass.constructor.call(this);
24563     // is conn a conn config or a real conn?
24564     this.conn = conn;
24565     this.useAjax = !conn || !conn.events;
24566   
24567 };
24568
24569 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24570     // thse are take from connection...
24571     
24572     /**
24573      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24574      */
24575     /**
24576      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24577      * extra parameters to each request made by this object. (defaults to undefined)
24578      */
24579     /**
24580      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24581      *  to each request made by this object. (defaults to undefined)
24582      */
24583     /**
24584      * @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)
24585      */
24586     /**
24587      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24588      */
24589      /**
24590      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24591      * @type Boolean
24592      */
24593   
24594
24595     /**
24596      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24597      * @type Boolean
24598      */
24599     /**
24600      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24601      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24602      * a finer-grained basis than the DataProxy events.
24603      */
24604     getConnection : function(){
24605         return this.useAjax ? Roo.Ajax : this.conn;
24606     },
24607
24608     /**
24609      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24610      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24611      * process that block using the passed callback.
24612      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24613      * for the request to the remote server.
24614      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24615      * object into a block of Roo.data.Records.
24616      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24617      * The function must be passed <ul>
24618      * <li>The Record block object</li>
24619      * <li>The "arg" argument from the load function</li>
24620      * <li>A boolean success indicator</li>
24621      * </ul>
24622      * @param {Object} scope The scope in which to call the callback
24623      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24624      */
24625     load : function(params, reader, callback, scope, arg){
24626         if(this.fireEvent("beforeload", this, params) !== false){
24627             var  o = {
24628                 params : params || {},
24629                 request: {
24630                     callback : callback,
24631                     scope : scope,
24632                     arg : arg
24633                 },
24634                 reader: reader,
24635                 callback : this.loadResponse,
24636                 scope: this
24637             };
24638             if(this.useAjax){
24639                 Roo.applyIf(o, this.conn);
24640                 if(this.activeRequest){
24641                     Roo.Ajax.abort(this.activeRequest);
24642                 }
24643                 this.activeRequest = Roo.Ajax.request(o);
24644             }else{
24645                 this.conn.request(o);
24646             }
24647         }else{
24648             callback.call(scope||this, null, arg, false);
24649         }
24650     },
24651
24652     // private
24653     loadResponse : function(o, success, response){
24654         delete this.activeRequest;
24655         if(!success){
24656             this.fireEvent("loadexception", this, o, response);
24657             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24658             return;
24659         }
24660         var result;
24661         try {
24662             result = o.reader.read(response);
24663         }catch(e){
24664             this.fireEvent("loadexception", this, o, response, e);
24665             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24666             return;
24667         }
24668         
24669         this.fireEvent("load", this, o, o.request.arg);
24670         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24671     },
24672
24673     // private
24674     update : function(dataSet){
24675
24676     },
24677
24678     // private
24679     updateResponse : function(dataSet){
24680
24681     }
24682 });/*
24683  * Based on:
24684  * Ext JS Library 1.1.1
24685  * Copyright(c) 2006-2007, Ext JS, LLC.
24686  *
24687  * Originally Released Under LGPL - original licence link has changed is not relivant.
24688  *
24689  * Fork - LGPL
24690  * <script type="text/javascript">
24691  */
24692
24693 /**
24694  * @class Roo.data.ScriptTagProxy
24695  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24696  * other than the originating domain of the running page.<br><br>
24697  * <p>
24698  * <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
24699  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24700  * <p>
24701  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24702  * source code that is used as the source inside a &lt;script> tag.<br><br>
24703  * <p>
24704  * In order for the browser to process the returned data, the server must wrap the data object
24705  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24706  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24707  * depending on whether the callback name was passed:
24708  * <p>
24709  * <pre><code>
24710 boolean scriptTag = false;
24711 String cb = request.getParameter("callback");
24712 if (cb != null) {
24713     scriptTag = true;
24714     response.setContentType("text/javascript");
24715 } else {
24716     response.setContentType("application/x-json");
24717 }
24718 Writer out = response.getWriter();
24719 if (scriptTag) {
24720     out.write(cb + "(");
24721 }
24722 out.print(dataBlock.toJsonString());
24723 if (scriptTag) {
24724     out.write(");");
24725 }
24726 </pre></code>
24727  *
24728  * @constructor
24729  * @param {Object} config A configuration object.
24730  */
24731 Roo.data.ScriptTagProxy = function(config){
24732     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24733     Roo.apply(this, config);
24734     this.head = document.getElementsByTagName("head")[0];
24735 };
24736
24737 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24738
24739 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24740     /**
24741      * @cfg {String} url The URL from which to request the data object.
24742      */
24743     /**
24744      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24745      */
24746     timeout : 30000,
24747     /**
24748      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24749      * the server the name of the callback function set up by the load call to process the returned data object.
24750      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24751      * javascript output which calls this named function passing the data object as its only parameter.
24752      */
24753     callbackParam : "callback",
24754     /**
24755      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24756      * name to the request.
24757      */
24758     nocache : true,
24759
24760     /**
24761      * Load data from the configured URL, read the data object into
24762      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24763      * process that block using the passed callback.
24764      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24765      * for the request to the remote server.
24766      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24767      * object into a block of Roo.data.Records.
24768      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24769      * The function must be passed <ul>
24770      * <li>The Record block object</li>
24771      * <li>The "arg" argument from the load function</li>
24772      * <li>A boolean success indicator</li>
24773      * </ul>
24774      * @param {Object} scope The scope in which to call the callback
24775      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24776      */
24777     load : function(params, reader, callback, scope, arg){
24778         if(this.fireEvent("beforeload", this, params) !== false){
24779
24780             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24781
24782             var url = this.url;
24783             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24784             if(this.nocache){
24785                 url += "&_dc=" + (new Date().getTime());
24786             }
24787             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24788             var trans = {
24789                 id : transId,
24790                 cb : "stcCallback"+transId,
24791                 scriptId : "stcScript"+transId,
24792                 params : params,
24793                 arg : arg,
24794                 url : url,
24795                 callback : callback,
24796                 scope : scope,
24797                 reader : reader
24798             };
24799             var conn = this;
24800
24801             window[trans.cb] = function(o){
24802                 conn.handleResponse(o, trans);
24803             };
24804
24805             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24806
24807             if(this.autoAbort !== false){
24808                 this.abort();
24809             }
24810
24811             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24812
24813             var script = document.createElement("script");
24814             script.setAttribute("src", url);
24815             script.setAttribute("type", "text/javascript");
24816             script.setAttribute("id", trans.scriptId);
24817             this.head.appendChild(script);
24818
24819             this.trans = trans;
24820         }else{
24821             callback.call(scope||this, null, arg, false);
24822         }
24823     },
24824
24825     // private
24826     isLoading : function(){
24827         return this.trans ? true : false;
24828     },
24829
24830     /**
24831      * Abort the current server request.
24832      */
24833     abort : function(){
24834         if(this.isLoading()){
24835             this.destroyTrans(this.trans);
24836         }
24837     },
24838
24839     // private
24840     destroyTrans : function(trans, isLoaded){
24841         this.head.removeChild(document.getElementById(trans.scriptId));
24842         clearTimeout(trans.timeoutId);
24843         if(isLoaded){
24844             window[trans.cb] = undefined;
24845             try{
24846                 delete window[trans.cb];
24847             }catch(e){}
24848         }else{
24849             // if hasn't been loaded, wait for load to remove it to prevent script error
24850             window[trans.cb] = function(){
24851                 window[trans.cb] = undefined;
24852                 try{
24853                     delete window[trans.cb];
24854                 }catch(e){}
24855             };
24856         }
24857     },
24858
24859     // private
24860     handleResponse : function(o, trans){
24861         this.trans = false;
24862         this.destroyTrans(trans, true);
24863         var result;
24864         try {
24865             result = trans.reader.readRecords(o);
24866         }catch(e){
24867             this.fireEvent("loadexception", this, o, trans.arg, e);
24868             trans.callback.call(trans.scope||window, null, trans.arg, false);
24869             return;
24870         }
24871         this.fireEvent("load", this, o, trans.arg);
24872         trans.callback.call(trans.scope||window, result, trans.arg, true);
24873     },
24874
24875     // private
24876     handleFailure : function(trans){
24877         this.trans = false;
24878         this.destroyTrans(trans, false);
24879         this.fireEvent("loadexception", this, null, trans.arg);
24880         trans.callback.call(trans.scope||window, null, trans.arg, false);
24881     }
24882 });/*
24883  * Based on:
24884  * Ext JS Library 1.1.1
24885  * Copyright(c) 2006-2007, Ext JS, LLC.
24886  *
24887  * Originally Released Under LGPL - original licence link has changed is not relivant.
24888  *
24889  * Fork - LGPL
24890  * <script type="text/javascript">
24891  */
24892
24893 /**
24894  * @class Roo.data.JsonReader
24895  * @extends Roo.data.DataReader
24896  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24897  * based on mappings in a provided Roo.data.Record constructor.
24898  * 
24899  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24900  * in the reply previously. 
24901  * 
24902  * <p>
24903  * Example code:
24904  * <pre><code>
24905 var RecordDef = Roo.data.Record.create([
24906     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24907     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24908 ]);
24909 var myReader = new Roo.data.JsonReader({
24910     totalProperty: "results",    // The property which contains the total dataset size (optional)
24911     root: "rows",                // The property which contains an Array of row objects
24912     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24913 }, RecordDef);
24914 </code></pre>
24915  * <p>
24916  * This would consume a JSON file like this:
24917  * <pre><code>
24918 { 'results': 2, 'rows': [
24919     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24920     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24921 }
24922 </code></pre>
24923  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24924  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24925  * paged from the remote server.
24926  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24927  * @cfg {String} root name of the property which contains the Array of row objects.
24928  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24929  * @cfg {Array} fields Array of field definition objects
24930  * @constructor
24931  * Create a new JsonReader
24932  * @param {Object} meta Metadata configuration options
24933  * @param {Object} recordType Either an Array of field definition objects,
24934  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24935  */
24936 Roo.data.JsonReader = function(meta, recordType){
24937     
24938     meta = meta || {};
24939     // set some defaults:
24940     Roo.applyIf(meta, {
24941         totalProperty: 'total',
24942         successProperty : 'success',
24943         root : 'data',
24944         id : 'id'
24945     });
24946     
24947     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
24948 };
24949 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
24950     
24951     readerType : 'Json',
24952     
24953     /**
24954      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
24955      * Used by Store query builder to append _requestMeta to params.
24956      * 
24957      */
24958     metaFromRemote : false,
24959     /**
24960      * This method is only used by a DataProxy which has retrieved data from a remote server.
24961      * @param {Object} response The XHR object which contains the JSON data in its responseText.
24962      * @return {Object} data A data block which is used by an Roo.data.Store object as
24963      * a cache of Roo.data.Records.
24964      */
24965     read : function(response){
24966         var json = response.responseText;
24967        
24968         var o = /* eval:var:o */ eval("("+json+")");
24969         if(!o) {
24970             throw {message: "JsonReader.read: Json object not found"};
24971         }
24972         
24973         if(o.metaData){
24974             
24975             delete this.ef;
24976             this.metaFromRemote = true;
24977             this.meta = o.metaData;
24978             this.recordType = Roo.data.Record.create(o.metaData.fields);
24979             this.onMetaChange(this.meta, this.recordType, o);
24980         }
24981         return this.readRecords(o);
24982     },
24983
24984     // private function a store will implement
24985     onMetaChange : function(meta, recordType, o){
24986
24987     },
24988
24989     /**
24990          * @ignore
24991          */
24992     simpleAccess: function(obj, subsc) {
24993         return obj[subsc];
24994     },
24995
24996         /**
24997          * @ignore
24998          */
24999     getJsonAccessor: function(){
25000         var re = /[\[\.]/;
25001         return function(expr) {
25002             try {
25003                 return(re.test(expr))
25004                     ? new Function("obj", "return obj." + expr)
25005                     : function(obj){
25006                         return obj[expr];
25007                     };
25008             } catch(e){}
25009             return Roo.emptyFn;
25010         };
25011     }(),
25012
25013     /**
25014      * Create a data block containing Roo.data.Records from an XML document.
25015      * @param {Object} o An object which contains an Array of row objects in the property specified
25016      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25017      * which contains the total size of the dataset.
25018      * @return {Object} data A data block which is used by an Roo.data.Store object as
25019      * a cache of Roo.data.Records.
25020      */
25021     readRecords : function(o){
25022         /**
25023          * After any data loads, the raw JSON data is available for further custom processing.
25024          * @type Object
25025          */
25026         this.o = o;
25027         var s = this.meta, Record = this.recordType,
25028             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25029
25030 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25031         if (!this.ef) {
25032             if(s.totalProperty) {
25033                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25034                 }
25035                 if(s.successProperty) {
25036                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25037                 }
25038                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25039                 if (s.id) {
25040                         var g = this.getJsonAccessor(s.id);
25041                         this.getId = function(rec) {
25042                                 var r = g(rec);  
25043                                 return (r === undefined || r === "") ? null : r;
25044                         };
25045                 } else {
25046                         this.getId = function(){return null;};
25047                 }
25048             this.ef = [];
25049             for(var jj = 0; jj < fl; jj++){
25050                 f = fi[jj];
25051                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25052                 this.ef[jj] = this.getJsonAccessor(map);
25053             }
25054         }
25055
25056         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25057         if(s.totalProperty){
25058             var vt = parseInt(this.getTotal(o), 10);
25059             if(!isNaN(vt)){
25060                 totalRecords = vt;
25061             }
25062         }
25063         if(s.successProperty){
25064             var vs = this.getSuccess(o);
25065             if(vs === false || vs === 'false'){
25066                 success = false;
25067             }
25068         }
25069         var records = [];
25070         for(var i = 0; i < c; i++){
25071                 var n = root[i];
25072             var values = {};
25073             var id = this.getId(n);
25074             for(var j = 0; j < fl; j++){
25075                 f = fi[j];
25076             var v = this.ef[j](n);
25077             if (!f.convert) {
25078                 Roo.log('missing convert for ' + f.name);
25079                 Roo.log(f);
25080                 continue;
25081             }
25082             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25083             }
25084             var record = new Record(values, id);
25085             record.json = n;
25086             records[i] = record;
25087         }
25088         return {
25089             raw : o,
25090             success : success,
25091             records : records,
25092             totalRecords : totalRecords
25093         };
25094     },
25095     // used when loading children.. @see loadDataFromChildren
25096     toLoadData: function(rec)
25097     {
25098         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25099         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25100         return { data : data, total : data.length };
25101         
25102     }
25103 });/*
25104  * Based on:
25105  * Ext JS Library 1.1.1
25106  * Copyright(c) 2006-2007, Ext JS, LLC.
25107  *
25108  * Originally Released Under LGPL - original licence link has changed is not relivant.
25109  *
25110  * Fork - LGPL
25111  * <script type="text/javascript">
25112  */
25113
25114 /**
25115  * @class Roo.data.XmlReader
25116  * @extends Roo.data.DataReader
25117  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25118  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25119  * <p>
25120  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25121  * header in the HTTP response must be set to "text/xml".</em>
25122  * <p>
25123  * Example code:
25124  * <pre><code>
25125 var RecordDef = Roo.data.Record.create([
25126    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25127    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25128 ]);
25129 var myReader = new Roo.data.XmlReader({
25130    totalRecords: "results", // The element which contains the total dataset size (optional)
25131    record: "row",           // The repeated element which contains row information
25132    id: "id"                 // The element within the row that provides an ID for the record (optional)
25133 }, RecordDef);
25134 </code></pre>
25135  * <p>
25136  * This would consume an XML file like this:
25137  * <pre><code>
25138 &lt;?xml?>
25139 &lt;dataset>
25140  &lt;results>2&lt;/results>
25141  &lt;row>
25142    &lt;id>1&lt;/id>
25143    &lt;name>Bill&lt;/name>
25144    &lt;occupation>Gardener&lt;/occupation>
25145  &lt;/row>
25146  &lt;row>
25147    &lt;id>2&lt;/id>
25148    &lt;name>Ben&lt;/name>
25149    &lt;occupation>Horticulturalist&lt;/occupation>
25150  &lt;/row>
25151 &lt;/dataset>
25152 </code></pre>
25153  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25154  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25155  * paged from the remote server.
25156  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25157  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25158  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25159  * a record identifier value.
25160  * @constructor
25161  * Create a new XmlReader
25162  * @param {Object} meta Metadata configuration options
25163  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25164  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25165  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25166  */
25167 Roo.data.XmlReader = function(meta, recordType){
25168     meta = meta || {};
25169     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25170 };
25171 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25172     
25173     readerType : 'Xml',
25174     
25175     /**
25176      * This method is only used by a DataProxy which has retrieved data from a remote server.
25177          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25178          * to contain a method called 'responseXML' that returns an XML document object.
25179      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25180      * a cache of Roo.data.Records.
25181      */
25182     read : function(response){
25183         var doc = response.responseXML;
25184         if(!doc) {
25185             throw {message: "XmlReader.read: XML Document not available"};
25186         }
25187         return this.readRecords(doc);
25188     },
25189
25190     /**
25191      * Create a data block containing Roo.data.Records from an XML document.
25192          * @param {Object} doc A parsed XML document.
25193      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25194      * a cache of Roo.data.Records.
25195      */
25196     readRecords : function(doc){
25197         /**
25198          * After any data loads/reads, the raw XML Document is available for further custom processing.
25199          * @type XMLDocument
25200          */
25201         this.xmlData = doc;
25202         var root = doc.documentElement || doc;
25203         var q = Roo.DomQuery;
25204         var recordType = this.recordType, fields = recordType.prototype.fields;
25205         var sid = this.meta.id;
25206         var totalRecords = 0, success = true;
25207         if(this.meta.totalRecords){
25208             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25209         }
25210         
25211         if(this.meta.success){
25212             var sv = q.selectValue(this.meta.success, root, true);
25213             success = sv !== false && sv !== 'false';
25214         }
25215         var records = [];
25216         var ns = q.select(this.meta.record, root);
25217         for(var i = 0, len = ns.length; i < len; i++) {
25218                 var n = ns[i];
25219                 var values = {};
25220                 var id = sid ? q.selectValue(sid, n) : undefined;
25221                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25222                     var f = fields.items[j];
25223                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25224                     v = f.convert(v);
25225                     values[f.name] = v;
25226                 }
25227                 var record = new recordType(values, id);
25228                 record.node = n;
25229                 records[records.length] = record;
25230             }
25231
25232             return {
25233                 success : success,
25234                 records : records,
25235                 totalRecords : totalRecords || records.length
25236             };
25237     }
25238 });/*
25239  * Based on:
25240  * Ext JS Library 1.1.1
25241  * Copyright(c) 2006-2007, Ext JS, LLC.
25242  *
25243  * Originally Released Under LGPL - original licence link has changed is not relivant.
25244  *
25245  * Fork - LGPL
25246  * <script type="text/javascript">
25247  */
25248
25249 /**
25250  * @class Roo.data.ArrayReader
25251  * @extends Roo.data.DataReader
25252  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25253  * Each element of that Array represents a row of data fields. The
25254  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25255  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25256  * <p>
25257  * Example code:.
25258  * <pre><code>
25259 var RecordDef = Roo.data.Record.create([
25260     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25261     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25262 ]);
25263 var myReader = new Roo.data.ArrayReader({
25264     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25265 }, RecordDef);
25266 </code></pre>
25267  * <p>
25268  * This would consume an Array like this:
25269  * <pre><code>
25270 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25271   </code></pre>
25272  
25273  * @constructor
25274  * Create a new JsonReader
25275  * @param {Object} meta Metadata configuration options.
25276  * @param {Object|Array} recordType Either an Array of field definition objects
25277  * 
25278  * @cfg {Array} fields Array of field definition objects
25279  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25280  * as specified to {@link Roo.data.Record#create},
25281  * or an {@link Roo.data.Record} object
25282  *
25283  * 
25284  * created using {@link Roo.data.Record#create}.
25285  */
25286 Roo.data.ArrayReader = function(meta, recordType)
25287 {    
25288     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25289 };
25290
25291 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25292     
25293       /**
25294      * Create a data block containing Roo.data.Records from an XML document.
25295      * @param {Object} o An Array of row objects which represents the dataset.
25296      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25297      * a cache of Roo.data.Records.
25298      */
25299     readRecords : function(o)
25300     {
25301         var sid = this.meta ? this.meta.id : null;
25302         var recordType = this.recordType, fields = recordType.prototype.fields;
25303         var records = [];
25304         var root = o;
25305         for(var i = 0; i < root.length; i++){
25306             var n = root[i];
25307             var values = {};
25308             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25309             for(var j = 0, jlen = fields.length; j < jlen; j++){
25310                 var f = fields.items[j];
25311                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25312                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25313                 v = f.convert(v);
25314                 values[f.name] = v;
25315             }
25316             var record = new recordType(values, id);
25317             record.json = n;
25318             records[records.length] = record;
25319         }
25320         return {
25321             records : records,
25322             totalRecords : records.length
25323         };
25324     },
25325     // used when loading children.. @see loadDataFromChildren
25326     toLoadData: function(rec)
25327     {
25328         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25329         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25330         
25331     }
25332     
25333     
25334 });/*
25335  * Based on:
25336  * Ext JS Library 1.1.1
25337  * Copyright(c) 2006-2007, Ext JS, LLC.
25338  *
25339  * Originally Released Under LGPL - original licence link has changed is not relivant.
25340  *
25341  * Fork - LGPL
25342  * <script type="text/javascript">
25343  */
25344
25345
25346 /**
25347  * @class Roo.data.Tree
25348  * @extends Roo.util.Observable
25349  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25350  * in the tree have most standard DOM functionality.
25351  * @constructor
25352  * @param {Node} root (optional) The root node
25353  */
25354 Roo.data.Tree = function(root){
25355    this.nodeHash = {};
25356    /**
25357     * The root node for this tree
25358     * @type Node
25359     */
25360    this.root = null;
25361    if(root){
25362        this.setRootNode(root);
25363    }
25364    this.addEvents({
25365        /**
25366         * @event append
25367         * Fires when a new child node is appended to a node in this tree.
25368         * @param {Tree} tree The owner tree
25369         * @param {Node} parent The parent node
25370         * @param {Node} node The newly appended node
25371         * @param {Number} index The index of the newly appended node
25372         */
25373        "append" : true,
25374        /**
25375         * @event remove
25376         * Fires when a child node is removed from a node in this tree.
25377         * @param {Tree} tree The owner tree
25378         * @param {Node} parent The parent node
25379         * @param {Node} node The child node removed
25380         */
25381        "remove" : true,
25382        /**
25383         * @event move
25384         * Fires when a node is moved to a new location in the tree
25385         * @param {Tree} tree The owner tree
25386         * @param {Node} node The node moved
25387         * @param {Node} oldParent The old parent of this node
25388         * @param {Node} newParent The new parent of this node
25389         * @param {Number} index The index it was moved to
25390         */
25391        "move" : true,
25392        /**
25393         * @event insert
25394         * Fires when a new child node is inserted in a node in this tree.
25395         * @param {Tree} tree The owner tree
25396         * @param {Node} parent The parent node
25397         * @param {Node} node The child node inserted
25398         * @param {Node} refNode The child node the node was inserted before
25399         */
25400        "insert" : true,
25401        /**
25402         * @event beforeappend
25403         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25404         * @param {Tree} tree The owner tree
25405         * @param {Node} parent The parent node
25406         * @param {Node} node The child node to be appended
25407         */
25408        "beforeappend" : true,
25409        /**
25410         * @event beforeremove
25411         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25412         * @param {Tree} tree The owner tree
25413         * @param {Node} parent The parent node
25414         * @param {Node} node The child node to be removed
25415         */
25416        "beforeremove" : true,
25417        /**
25418         * @event beforemove
25419         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25420         * @param {Tree} tree The owner tree
25421         * @param {Node} node The node being moved
25422         * @param {Node} oldParent The parent of the node
25423         * @param {Node} newParent The new parent the node is moving to
25424         * @param {Number} index The index it is being moved to
25425         */
25426        "beforemove" : true,
25427        /**
25428         * @event beforeinsert
25429         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25430         * @param {Tree} tree The owner tree
25431         * @param {Node} parent The parent node
25432         * @param {Node} node The child node to be inserted
25433         * @param {Node} refNode The child node the node is being inserted before
25434         */
25435        "beforeinsert" : true
25436    });
25437
25438     Roo.data.Tree.superclass.constructor.call(this);
25439 };
25440
25441 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25442     pathSeparator: "/",
25443
25444     proxyNodeEvent : function(){
25445         return this.fireEvent.apply(this, arguments);
25446     },
25447
25448     /**
25449      * Returns the root node for this tree.
25450      * @return {Node}
25451      */
25452     getRootNode : function(){
25453         return this.root;
25454     },
25455
25456     /**
25457      * Sets the root node for this tree.
25458      * @param {Node} node
25459      * @return {Node}
25460      */
25461     setRootNode : function(node){
25462         this.root = node;
25463         node.ownerTree = this;
25464         node.isRoot = true;
25465         this.registerNode(node);
25466         return node;
25467     },
25468
25469     /**
25470      * Gets a node in this tree by its id.
25471      * @param {String} id
25472      * @return {Node}
25473      */
25474     getNodeById : function(id){
25475         return this.nodeHash[id];
25476     },
25477
25478     registerNode : function(node){
25479         this.nodeHash[node.id] = node;
25480     },
25481
25482     unregisterNode : function(node){
25483         delete this.nodeHash[node.id];
25484     },
25485
25486     toString : function(){
25487         return "[Tree"+(this.id?" "+this.id:"")+"]";
25488     }
25489 });
25490
25491 /**
25492  * @class Roo.data.Node
25493  * @extends Roo.util.Observable
25494  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25495  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25496  * @constructor
25497  * @param {Object} attributes The attributes/config for the node
25498  */
25499 Roo.data.Node = function(attributes){
25500     /**
25501      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25502      * @type {Object}
25503      */
25504     this.attributes = attributes || {};
25505     this.leaf = this.attributes.leaf;
25506     /**
25507      * The node id. @type String
25508      */
25509     this.id = this.attributes.id;
25510     if(!this.id){
25511         this.id = Roo.id(null, "ynode-");
25512         this.attributes.id = this.id;
25513     }
25514      
25515     
25516     /**
25517      * All child nodes of this node. @type Array
25518      */
25519     this.childNodes = [];
25520     if(!this.childNodes.indexOf){ // indexOf is a must
25521         this.childNodes.indexOf = function(o){
25522             for(var i = 0, len = this.length; i < len; i++){
25523                 if(this[i] == o) {
25524                     return i;
25525                 }
25526             }
25527             return -1;
25528         };
25529     }
25530     /**
25531      * The parent node for this node. @type Node
25532      */
25533     this.parentNode = null;
25534     /**
25535      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25536      */
25537     this.firstChild = null;
25538     /**
25539      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25540      */
25541     this.lastChild = null;
25542     /**
25543      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25544      */
25545     this.previousSibling = null;
25546     /**
25547      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25548      */
25549     this.nextSibling = null;
25550
25551     this.addEvents({
25552        /**
25553         * @event append
25554         * Fires when a new child node is appended
25555         * @param {Tree} tree The owner tree
25556         * @param {Node} this This node
25557         * @param {Node} node The newly appended node
25558         * @param {Number} index The index of the newly appended node
25559         */
25560        "append" : true,
25561        /**
25562         * @event remove
25563         * Fires when a child node is removed
25564         * @param {Tree} tree The owner tree
25565         * @param {Node} this This node
25566         * @param {Node} node The removed node
25567         */
25568        "remove" : true,
25569        /**
25570         * @event move
25571         * Fires when this node is moved to a new location in the tree
25572         * @param {Tree} tree The owner tree
25573         * @param {Node} this This node
25574         * @param {Node} oldParent The old parent of this node
25575         * @param {Node} newParent The new parent of this node
25576         * @param {Number} index The index it was moved to
25577         */
25578        "move" : true,
25579        /**
25580         * @event insert
25581         * Fires when a new child node is inserted.
25582         * @param {Tree} tree The owner tree
25583         * @param {Node} this This node
25584         * @param {Node} node The child node inserted
25585         * @param {Node} refNode The child node the node was inserted before
25586         */
25587        "insert" : true,
25588        /**
25589         * @event beforeappend
25590         * Fires before a new child is appended, return false to cancel the append.
25591         * @param {Tree} tree The owner tree
25592         * @param {Node} this This node
25593         * @param {Node} node The child node to be appended
25594         */
25595        "beforeappend" : true,
25596        /**
25597         * @event beforeremove
25598         * Fires before a child is removed, return false to cancel the remove.
25599         * @param {Tree} tree The owner tree
25600         * @param {Node} this This node
25601         * @param {Node} node The child node to be removed
25602         */
25603        "beforeremove" : true,
25604        /**
25605         * @event beforemove
25606         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25607         * @param {Tree} tree The owner tree
25608         * @param {Node} this This node
25609         * @param {Node} oldParent The parent of this node
25610         * @param {Node} newParent The new parent this node is moving to
25611         * @param {Number} index The index it is being moved to
25612         */
25613        "beforemove" : true,
25614        /**
25615         * @event beforeinsert
25616         * Fires before a new child is inserted, return false to cancel the insert.
25617         * @param {Tree} tree The owner tree
25618         * @param {Node} this This node
25619         * @param {Node} node The child node to be inserted
25620         * @param {Node} refNode The child node the node is being inserted before
25621         */
25622        "beforeinsert" : true
25623    });
25624     this.listeners = this.attributes.listeners;
25625     Roo.data.Node.superclass.constructor.call(this);
25626 };
25627
25628 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25629     fireEvent : function(evtName){
25630         // first do standard event for this node
25631         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25632             return false;
25633         }
25634         // then bubble it up to the tree if the event wasn't cancelled
25635         var ot = this.getOwnerTree();
25636         if(ot){
25637             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25638                 return false;
25639             }
25640         }
25641         return true;
25642     },
25643
25644     /**
25645      * Returns true if this node is a leaf
25646      * @return {Boolean}
25647      */
25648     isLeaf : function(){
25649         return this.leaf === true;
25650     },
25651
25652     // private
25653     setFirstChild : function(node){
25654         this.firstChild = node;
25655     },
25656
25657     //private
25658     setLastChild : function(node){
25659         this.lastChild = node;
25660     },
25661
25662
25663     /**
25664      * Returns true if this node is the last child of its parent
25665      * @return {Boolean}
25666      */
25667     isLast : function(){
25668        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25669     },
25670
25671     /**
25672      * Returns true if this node is the first child of its parent
25673      * @return {Boolean}
25674      */
25675     isFirst : function(){
25676        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25677     },
25678
25679     hasChildNodes : function(){
25680         return !this.isLeaf() && this.childNodes.length > 0;
25681     },
25682
25683     /**
25684      * Insert node(s) as the last child node of this node.
25685      * @param {Node/Array} node The node or Array of nodes to append
25686      * @return {Node} The appended node if single append, or null if an array was passed
25687      */
25688     appendChild : function(node){
25689         var multi = false;
25690         if(node instanceof Array){
25691             multi = node;
25692         }else if(arguments.length > 1){
25693             multi = arguments;
25694         }
25695         
25696         // if passed an array or multiple args do them one by one
25697         if(multi){
25698             for(var i = 0, len = multi.length; i < len; i++) {
25699                 this.appendChild(multi[i]);
25700             }
25701         }else{
25702             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25703                 return false;
25704             }
25705             var index = this.childNodes.length;
25706             var oldParent = node.parentNode;
25707             // it's a move, make sure we move it cleanly
25708             if(oldParent){
25709                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25710                     return false;
25711                 }
25712                 oldParent.removeChild(node);
25713             }
25714             
25715             index = this.childNodes.length;
25716             if(index == 0){
25717                 this.setFirstChild(node);
25718             }
25719             this.childNodes.push(node);
25720             node.parentNode = this;
25721             var ps = this.childNodes[index-1];
25722             if(ps){
25723                 node.previousSibling = ps;
25724                 ps.nextSibling = node;
25725             }else{
25726                 node.previousSibling = null;
25727             }
25728             node.nextSibling = null;
25729             this.setLastChild(node);
25730             node.setOwnerTree(this.getOwnerTree());
25731             this.fireEvent("append", this.ownerTree, this, node, index);
25732             if(this.ownerTree) {
25733                 this.ownerTree.fireEvent("appendnode", this, node, index);
25734             }
25735             if(oldParent){
25736                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25737             }
25738             return node;
25739         }
25740     },
25741
25742     /**
25743      * Removes a child node from this node.
25744      * @param {Node} node The node to remove
25745      * @return {Node} The removed node
25746      */
25747     removeChild : function(node){
25748         var index = this.childNodes.indexOf(node);
25749         if(index == -1){
25750             return false;
25751         }
25752         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25753             return false;
25754         }
25755
25756         // remove it from childNodes collection
25757         this.childNodes.splice(index, 1);
25758
25759         // update siblings
25760         if(node.previousSibling){
25761             node.previousSibling.nextSibling = node.nextSibling;
25762         }
25763         if(node.nextSibling){
25764             node.nextSibling.previousSibling = node.previousSibling;
25765         }
25766
25767         // update child refs
25768         if(this.firstChild == node){
25769             this.setFirstChild(node.nextSibling);
25770         }
25771         if(this.lastChild == node){
25772             this.setLastChild(node.previousSibling);
25773         }
25774
25775         node.setOwnerTree(null);
25776         // clear any references from the node
25777         node.parentNode = null;
25778         node.previousSibling = null;
25779         node.nextSibling = null;
25780         this.fireEvent("remove", this.ownerTree, this, node);
25781         return node;
25782     },
25783
25784     /**
25785      * Inserts the first node before the second node in this nodes childNodes collection.
25786      * @param {Node} node The node to insert
25787      * @param {Node} refNode The node to insert before (if null the node is appended)
25788      * @return {Node} The inserted node
25789      */
25790     insertBefore : function(node, refNode){
25791         if(!refNode){ // like standard Dom, refNode can be null for append
25792             return this.appendChild(node);
25793         }
25794         // nothing to do
25795         if(node == refNode){
25796             return false;
25797         }
25798
25799         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25800             return false;
25801         }
25802         var index = this.childNodes.indexOf(refNode);
25803         var oldParent = node.parentNode;
25804         var refIndex = index;
25805
25806         // when moving internally, indexes will change after remove
25807         if(oldParent == this && this.childNodes.indexOf(node) < index){
25808             refIndex--;
25809         }
25810
25811         // it's a move, make sure we move it cleanly
25812         if(oldParent){
25813             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25814                 return false;
25815             }
25816             oldParent.removeChild(node);
25817         }
25818         if(refIndex == 0){
25819             this.setFirstChild(node);
25820         }
25821         this.childNodes.splice(refIndex, 0, node);
25822         node.parentNode = this;
25823         var ps = this.childNodes[refIndex-1];
25824         if(ps){
25825             node.previousSibling = ps;
25826             ps.nextSibling = node;
25827         }else{
25828             node.previousSibling = null;
25829         }
25830         node.nextSibling = refNode;
25831         refNode.previousSibling = node;
25832         node.setOwnerTree(this.getOwnerTree());
25833         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25834         if(oldParent){
25835             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25836         }
25837         return node;
25838     },
25839
25840     /**
25841      * Returns the child node at the specified index.
25842      * @param {Number} index
25843      * @return {Node}
25844      */
25845     item : function(index){
25846         return this.childNodes[index];
25847     },
25848
25849     /**
25850      * Replaces one child node in this node with another.
25851      * @param {Node} newChild The replacement node
25852      * @param {Node} oldChild The node to replace
25853      * @return {Node} The replaced node
25854      */
25855     replaceChild : function(newChild, oldChild){
25856         this.insertBefore(newChild, oldChild);
25857         this.removeChild(oldChild);
25858         return oldChild;
25859     },
25860
25861     /**
25862      * Returns the index of a child node
25863      * @param {Node} node
25864      * @return {Number} The index of the node or -1 if it was not found
25865      */
25866     indexOf : function(child){
25867         return this.childNodes.indexOf(child);
25868     },
25869
25870     /**
25871      * Returns the tree this node is in.
25872      * @return {Tree}
25873      */
25874     getOwnerTree : function(){
25875         // if it doesn't have one, look for one
25876         if(!this.ownerTree){
25877             var p = this;
25878             while(p){
25879                 if(p.ownerTree){
25880                     this.ownerTree = p.ownerTree;
25881                     break;
25882                 }
25883                 p = p.parentNode;
25884             }
25885         }
25886         return this.ownerTree;
25887     },
25888
25889     /**
25890      * Returns depth of this node (the root node has a depth of 0)
25891      * @return {Number}
25892      */
25893     getDepth : function(){
25894         var depth = 0;
25895         var p = this;
25896         while(p.parentNode){
25897             ++depth;
25898             p = p.parentNode;
25899         }
25900         return depth;
25901     },
25902
25903     // private
25904     setOwnerTree : function(tree){
25905         // if it's move, we need to update everyone
25906         if(tree != this.ownerTree){
25907             if(this.ownerTree){
25908                 this.ownerTree.unregisterNode(this);
25909             }
25910             this.ownerTree = tree;
25911             var cs = this.childNodes;
25912             for(var i = 0, len = cs.length; i < len; i++) {
25913                 cs[i].setOwnerTree(tree);
25914             }
25915             if(tree){
25916                 tree.registerNode(this);
25917             }
25918         }
25919     },
25920
25921     /**
25922      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25923      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25924      * @return {String} The path
25925      */
25926     getPath : function(attr){
25927         attr = attr || "id";
25928         var p = this.parentNode;
25929         var b = [this.attributes[attr]];
25930         while(p){
25931             b.unshift(p.attributes[attr]);
25932             p = p.parentNode;
25933         }
25934         var sep = this.getOwnerTree().pathSeparator;
25935         return sep + b.join(sep);
25936     },
25937
25938     /**
25939      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25940      * function call will be the scope provided or the current node. The arguments to the function
25941      * will be the args provided or the current node. If the function returns false at any point,
25942      * the bubble is stopped.
25943      * @param {Function} fn The function to call
25944      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25945      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25946      */
25947     bubble : function(fn, scope, args){
25948         var p = this;
25949         while(p){
25950             if(fn.call(scope || p, args || p) === false){
25951                 break;
25952             }
25953             p = p.parentNode;
25954         }
25955     },
25956
25957     /**
25958      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
25959      * function call will be the scope provided or the current node. The arguments to the function
25960      * will be the args provided or the current node. If the function returns false at any point,
25961      * the cascade is stopped on that branch.
25962      * @param {Function} fn The function to call
25963      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25964      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25965      */
25966     cascade : function(fn, scope, args){
25967         if(fn.call(scope || this, args || this) !== false){
25968             var cs = this.childNodes;
25969             for(var i = 0, len = cs.length; i < len; i++) {
25970                 cs[i].cascade(fn, scope, args);
25971             }
25972         }
25973     },
25974
25975     /**
25976      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
25977      * function call will be the scope provided or the current node. The arguments to the function
25978      * will be the args provided or the current node. If the function returns false at any point,
25979      * the iteration stops.
25980      * @param {Function} fn The function to call
25981      * @param {Object} scope (optional) The scope of the function (defaults to current node)
25982      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
25983      */
25984     eachChild : function(fn, scope, args){
25985         var cs = this.childNodes;
25986         for(var i = 0, len = cs.length; i < len; i++) {
25987                 if(fn.call(scope || this, args || cs[i]) === false){
25988                     break;
25989                 }
25990         }
25991     },
25992
25993     /**
25994      * Finds the first child that has the attribute with the specified value.
25995      * @param {String} attribute The attribute name
25996      * @param {Mixed} value The value to search for
25997      * @return {Node} The found child or null if none was found
25998      */
25999     findChild : function(attribute, value){
26000         var cs = this.childNodes;
26001         for(var i = 0, len = cs.length; i < len; i++) {
26002                 if(cs[i].attributes[attribute] == value){
26003                     return cs[i];
26004                 }
26005         }
26006         return null;
26007     },
26008
26009     /**
26010      * Finds the first child by a custom function. The child matches if the function passed
26011      * returns true.
26012      * @param {Function} fn
26013      * @param {Object} scope (optional)
26014      * @return {Node} The found child or null if none was found
26015      */
26016     findChildBy : function(fn, scope){
26017         var cs = this.childNodes;
26018         for(var i = 0, len = cs.length; i < len; i++) {
26019                 if(fn.call(scope||cs[i], cs[i]) === true){
26020                     return cs[i];
26021                 }
26022         }
26023         return null;
26024     },
26025
26026     /**
26027      * Sorts this nodes children using the supplied sort function
26028      * @param {Function} fn
26029      * @param {Object} scope (optional)
26030      */
26031     sort : function(fn, scope){
26032         var cs = this.childNodes;
26033         var len = cs.length;
26034         if(len > 0){
26035             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26036             cs.sort(sortFn);
26037             for(var i = 0; i < len; i++){
26038                 var n = cs[i];
26039                 n.previousSibling = cs[i-1];
26040                 n.nextSibling = cs[i+1];
26041                 if(i == 0){
26042                     this.setFirstChild(n);
26043                 }
26044                 if(i == len-1){
26045                     this.setLastChild(n);
26046                 }
26047             }
26048         }
26049     },
26050
26051     /**
26052      * Returns true if this node is an ancestor (at any point) of the passed node.
26053      * @param {Node} node
26054      * @return {Boolean}
26055      */
26056     contains : function(node){
26057         return node.isAncestor(this);
26058     },
26059
26060     /**
26061      * Returns true if the passed node is an ancestor (at any point) of this node.
26062      * @param {Node} node
26063      * @return {Boolean}
26064      */
26065     isAncestor : function(node){
26066         var p = this.parentNode;
26067         while(p){
26068             if(p == node){
26069                 return true;
26070             }
26071             p = p.parentNode;
26072         }
26073         return false;
26074     },
26075
26076     toString : function(){
26077         return "[Node"+(this.id?" "+this.id:"")+"]";
26078     }
26079 });/*
26080  * Based on:
26081  * Ext JS Library 1.1.1
26082  * Copyright(c) 2006-2007, Ext JS, LLC.
26083  *
26084  * Originally Released Under LGPL - original licence link has changed is not relivant.
26085  *
26086  * Fork - LGPL
26087  * <script type="text/javascript">
26088  */
26089
26090
26091 /**
26092  * @class Roo.Shadow
26093  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26094  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26095  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26096  * @constructor
26097  * Create a new Shadow
26098  * @param {Object} config The config object
26099  */
26100 Roo.Shadow = function(config){
26101     Roo.apply(this, config);
26102     if(typeof this.mode != "string"){
26103         this.mode = this.defaultMode;
26104     }
26105     var o = this.offset, a = {h: 0};
26106     var rad = Math.floor(this.offset/2);
26107     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26108         case "drop":
26109             a.w = 0;
26110             a.l = a.t = o;
26111             a.t -= 1;
26112             if(Roo.isIE){
26113                 a.l -= this.offset + rad;
26114                 a.t -= this.offset + rad;
26115                 a.w -= rad;
26116                 a.h -= rad;
26117                 a.t += 1;
26118             }
26119         break;
26120         case "sides":
26121             a.w = (o*2);
26122             a.l = -o;
26123             a.t = o-1;
26124             if(Roo.isIE){
26125                 a.l -= (this.offset - rad);
26126                 a.t -= this.offset + rad;
26127                 a.l += 1;
26128                 a.w -= (this.offset - rad)*2;
26129                 a.w -= rad + 1;
26130                 a.h -= 1;
26131             }
26132         break;
26133         case "frame":
26134             a.w = a.h = (o*2);
26135             a.l = a.t = -o;
26136             a.t += 1;
26137             a.h -= 2;
26138             if(Roo.isIE){
26139                 a.l -= (this.offset - rad);
26140                 a.t -= (this.offset - rad);
26141                 a.l += 1;
26142                 a.w -= (this.offset + rad + 1);
26143                 a.h -= (this.offset + rad);
26144                 a.h += 1;
26145             }
26146         break;
26147     };
26148
26149     this.adjusts = a;
26150 };
26151
26152 Roo.Shadow.prototype = {
26153     /**
26154      * @cfg {String} mode
26155      * The shadow display mode.  Supports the following options:<br />
26156      * sides: Shadow displays on both sides and bottom only<br />
26157      * frame: Shadow displays equally on all four sides<br />
26158      * drop: Traditional bottom-right drop shadow (default)
26159      */
26160     /**
26161      * @cfg {String} offset
26162      * The number of pixels to offset the shadow from the element (defaults to 4)
26163      */
26164     offset: 4,
26165
26166     // private
26167     defaultMode: "drop",
26168
26169     /**
26170      * Displays the shadow under the target element
26171      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26172      */
26173     show : function(target){
26174         target = Roo.get(target);
26175         if(!this.el){
26176             this.el = Roo.Shadow.Pool.pull();
26177             if(this.el.dom.nextSibling != target.dom){
26178                 this.el.insertBefore(target);
26179             }
26180         }
26181         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26182         if(Roo.isIE){
26183             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26184         }
26185         this.realign(
26186             target.getLeft(true),
26187             target.getTop(true),
26188             target.getWidth(),
26189             target.getHeight()
26190         );
26191         this.el.dom.style.display = "block";
26192     },
26193
26194     /**
26195      * Returns true if the shadow is visible, else false
26196      */
26197     isVisible : function(){
26198         return this.el ? true : false;  
26199     },
26200
26201     /**
26202      * Direct alignment when values are already available. Show must be called at least once before
26203      * calling this method to ensure it is initialized.
26204      * @param {Number} left The target element left position
26205      * @param {Number} top The target element top position
26206      * @param {Number} width The target element width
26207      * @param {Number} height The target element height
26208      */
26209     realign : function(l, t, w, h){
26210         if(!this.el){
26211             return;
26212         }
26213         var a = this.adjusts, d = this.el.dom, s = d.style;
26214         var iea = 0;
26215         s.left = (l+a.l)+"px";
26216         s.top = (t+a.t)+"px";
26217         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26218  
26219         if(s.width != sws || s.height != shs){
26220             s.width = sws;
26221             s.height = shs;
26222             if(!Roo.isIE){
26223                 var cn = d.childNodes;
26224                 var sww = Math.max(0, (sw-12))+"px";
26225                 cn[0].childNodes[1].style.width = sww;
26226                 cn[1].childNodes[1].style.width = sww;
26227                 cn[2].childNodes[1].style.width = sww;
26228                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26229             }
26230         }
26231     },
26232
26233     /**
26234      * Hides this shadow
26235      */
26236     hide : function(){
26237         if(this.el){
26238             this.el.dom.style.display = "none";
26239             Roo.Shadow.Pool.push(this.el);
26240             delete this.el;
26241         }
26242     },
26243
26244     /**
26245      * Adjust the z-index of this shadow
26246      * @param {Number} zindex The new z-index
26247      */
26248     setZIndex : function(z){
26249         this.zIndex = z;
26250         if(this.el){
26251             this.el.setStyle("z-index", z);
26252         }
26253     }
26254 };
26255
26256 // Private utility class that manages the internal Shadow cache
26257 Roo.Shadow.Pool = function(){
26258     var p = [];
26259     var markup = Roo.isIE ?
26260                  '<div class="x-ie-shadow"></div>' :
26261                  '<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>';
26262     return {
26263         pull : function(){
26264             var sh = p.shift();
26265             if(!sh){
26266                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26267                 sh.autoBoxAdjust = false;
26268             }
26269             return sh;
26270         },
26271
26272         push : function(sh){
26273             p.push(sh);
26274         }
26275     };
26276 }();/*
26277  * Based on:
26278  * Ext JS Library 1.1.1
26279  * Copyright(c) 2006-2007, Ext JS, LLC.
26280  *
26281  * Originally Released Under LGPL - original licence link has changed is not relivant.
26282  *
26283  * Fork - LGPL
26284  * <script type="text/javascript">
26285  */
26286
26287
26288 /**
26289  * @class Roo.SplitBar
26290  * @extends Roo.util.Observable
26291  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26292  * <br><br>
26293  * Usage:
26294  * <pre><code>
26295 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26296                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26297 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26298 split.minSize = 100;
26299 split.maxSize = 600;
26300 split.animate = true;
26301 split.on('moved', splitterMoved);
26302 </code></pre>
26303  * @constructor
26304  * Create a new SplitBar
26305  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26306  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26307  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26308  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26309                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26310                         position of the SplitBar).
26311  */
26312 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26313     
26314     /** @private */
26315     this.el = Roo.get(dragElement, true);
26316     this.el.dom.unselectable = "on";
26317     /** @private */
26318     this.resizingEl = Roo.get(resizingElement, true);
26319
26320     /**
26321      * @private
26322      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26323      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26324      * @type Number
26325      */
26326     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26327     
26328     /**
26329      * The minimum size of the resizing element. (Defaults to 0)
26330      * @type Number
26331      */
26332     this.minSize = 0;
26333     
26334     /**
26335      * The maximum size of the resizing element. (Defaults to 2000)
26336      * @type Number
26337      */
26338     this.maxSize = 2000;
26339     
26340     /**
26341      * Whether to animate the transition to the new size
26342      * @type Boolean
26343      */
26344     this.animate = false;
26345     
26346     /**
26347      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26348      * @type Boolean
26349      */
26350     this.useShim = false;
26351     
26352     /** @private */
26353     this.shim = null;
26354     
26355     if(!existingProxy){
26356         /** @private */
26357         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26358     }else{
26359         this.proxy = Roo.get(existingProxy).dom;
26360     }
26361     /** @private */
26362     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26363     
26364     /** @private */
26365     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26366     
26367     /** @private */
26368     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26369     
26370     /** @private */
26371     this.dragSpecs = {};
26372     
26373     /**
26374      * @private The adapter to use to positon and resize elements
26375      */
26376     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26377     this.adapter.init(this);
26378     
26379     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26380         /** @private */
26381         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26382         this.el.addClass("x-splitbar-h");
26383     }else{
26384         /** @private */
26385         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26386         this.el.addClass("x-splitbar-v");
26387     }
26388     
26389     this.addEvents({
26390         /**
26391          * @event resize
26392          * Fires when the splitter is moved (alias for {@link #event-moved})
26393          * @param {Roo.SplitBar} this
26394          * @param {Number} newSize the new width or height
26395          */
26396         "resize" : true,
26397         /**
26398          * @event moved
26399          * Fires when the splitter is moved
26400          * @param {Roo.SplitBar} this
26401          * @param {Number} newSize the new width or height
26402          */
26403         "moved" : true,
26404         /**
26405          * @event beforeresize
26406          * Fires before the splitter is dragged
26407          * @param {Roo.SplitBar} this
26408          */
26409         "beforeresize" : true,
26410
26411         "beforeapply" : true
26412     });
26413
26414     Roo.util.Observable.call(this);
26415 };
26416
26417 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26418     onStartProxyDrag : function(x, y){
26419         this.fireEvent("beforeresize", this);
26420         if(!this.overlay){
26421             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26422             o.unselectable();
26423             o.enableDisplayMode("block");
26424             // all splitbars share the same overlay
26425             Roo.SplitBar.prototype.overlay = o;
26426         }
26427         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26428         this.overlay.show();
26429         Roo.get(this.proxy).setDisplayed("block");
26430         var size = this.adapter.getElementSize(this);
26431         this.activeMinSize = this.getMinimumSize();;
26432         this.activeMaxSize = this.getMaximumSize();;
26433         var c1 = size - this.activeMinSize;
26434         var c2 = Math.max(this.activeMaxSize - size, 0);
26435         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26436             this.dd.resetConstraints();
26437             this.dd.setXConstraint(
26438                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26439                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26440             );
26441             this.dd.setYConstraint(0, 0);
26442         }else{
26443             this.dd.resetConstraints();
26444             this.dd.setXConstraint(0, 0);
26445             this.dd.setYConstraint(
26446                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26447                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26448             );
26449          }
26450         this.dragSpecs.startSize = size;
26451         this.dragSpecs.startPoint = [x, y];
26452         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26453     },
26454     
26455     /** 
26456      * @private Called after the drag operation by the DDProxy
26457      */
26458     onEndProxyDrag : function(e){
26459         Roo.get(this.proxy).setDisplayed(false);
26460         var endPoint = Roo.lib.Event.getXY(e);
26461         if(this.overlay){
26462             this.overlay.hide();
26463         }
26464         var newSize;
26465         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26466             newSize = this.dragSpecs.startSize + 
26467                 (this.placement == Roo.SplitBar.LEFT ?
26468                     endPoint[0] - this.dragSpecs.startPoint[0] :
26469                     this.dragSpecs.startPoint[0] - endPoint[0]
26470                 );
26471         }else{
26472             newSize = this.dragSpecs.startSize + 
26473                 (this.placement == Roo.SplitBar.TOP ?
26474                     endPoint[1] - this.dragSpecs.startPoint[1] :
26475                     this.dragSpecs.startPoint[1] - endPoint[1]
26476                 );
26477         }
26478         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26479         if(newSize != this.dragSpecs.startSize){
26480             if(this.fireEvent('beforeapply', this, newSize) !== false){
26481                 this.adapter.setElementSize(this, newSize);
26482                 this.fireEvent("moved", this, newSize);
26483                 this.fireEvent("resize", this, newSize);
26484             }
26485         }
26486     },
26487     
26488     /**
26489      * Get the adapter this SplitBar uses
26490      * @return The adapter object
26491      */
26492     getAdapter : function(){
26493         return this.adapter;
26494     },
26495     
26496     /**
26497      * Set the adapter this SplitBar uses
26498      * @param {Object} adapter A SplitBar adapter object
26499      */
26500     setAdapter : function(adapter){
26501         this.adapter = adapter;
26502         this.adapter.init(this);
26503     },
26504     
26505     /**
26506      * Gets the minimum size for the resizing element
26507      * @return {Number} The minimum size
26508      */
26509     getMinimumSize : function(){
26510         return this.minSize;
26511     },
26512     
26513     /**
26514      * Sets the minimum size for the resizing element
26515      * @param {Number} minSize The minimum size
26516      */
26517     setMinimumSize : function(minSize){
26518         this.minSize = minSize;
26519     },
26520     
26521     /**
26522      * Gets the maximum size for the resizing element
26523      * @return {Number} The maximum size
26524      */
26525     getMaximumSize : function(){
26526         return this.maxSize;
26527     },
26528     
26529     /**
26530      * Sets the maximum size for the resizing element
26531      * @param {Number} maxSize The maximum size
26532      */
26533     setMaximumSize : function(maxSize){
26534         this.maxSize = maxSize;
26535     },
26536     
26537     /**
26538      * Sets the initialize size for the resizing element
26539      * @param {Number} size The initial size
26540      */
26541     setCurrentSize : function(size){
26542         var oldAnimate = this.animate;
26543         this.animate = false;
26544         this.adapter.setElementSize(this, size);
26545         this.animate = oldAnimate;
26546     },
26547     
26548     /**
26549      * Destroy this splitbar. 
26550      * @param {Boolean} removeEl True to remove the element
26551      */
26552     destroy : function(removeEl){
26553         if(this.shim){
26554             this.shim.remove();
26555         }
26556         this.dd.unreg();
26557         this.proxy.parentNode.removeChild(this.proxy);
26558         if(removeEl){
26559             this.el.remove();
26560         }
26561     }
26562 });
26563
26564 /**
26565  * @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.
26566  */
26567 Roo.SplitBar.createProxy = function(dir){
26568     var proxy = new Roo.Element(document.createElement("div"));
26569     proxy.unselectable();
26570     var cls = 'x-splitbar-proxy';
26571     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26572     document.body.appendChild(proxy.dom);
26573     return proxy.dom;
26574 };
26575
26576 /** 
26577  * @class Roo.SplitBar.BasicLayoutAdapter
26578  * Default Adapter. It assumes the splitter and resizing element are not positioned
26579  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26580  */
26581 Roo.SplitBar.BasicLayoutAdapter = function(){
26582 };
26583
26584 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26585     // do nothing for now
26586     init : function(s){
26587     
26588     },
26589     /**
26590      * Called before drag operations to get the current size of the resizing element. 
26591      * @param {Roo.SplitBar} s The SplitBar using this adapter
26592      */
26593      getElementSize : function(s){
26594         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26595             return s.resizingEl.getWidth();
26596         }else{
26597             return s.resizingEl.getHeight();
26598         }
26599     },
26600     
26601     /**
26602      * Called after drag operations to set the size of the resizing element.
26603      * @param {Roo.SplitBar} s The SplitBar using this adapter
26604      * @param {Number} newSize The new size to set
26605      * @param {Function} onComplete A function to be invoked when resizing is complete
26606      */
26607     setElementSize : function(s, newSize, onComplete){
26608         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26609             if(!s.animate){
26610                 s.resizingEl.setWidth(newSize);
26611                 if(onComplete){
26612                     onComplete(s, newSize);
26613                 }
26614             }else{
26615                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26616             }
26617         }else{
26618             
26619             if(!s.animate){
26620                 s.resizingEl.setHeight(newSize);
26621                 if(onComplete){
26622                     onComplete(s, newSize);
26623                 }
26624             }else{
26625                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26626             }
26627         }
26628     }
26629 };
26630
26631 /** 
26632  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26633  * @extends Roo.SplitBar.BasicLayoutAdapter
26634  * Adapter that  moves the splitter element to align with the resized sizing element. 
26635  * Used with an absolute positioned SplitBar.
26636  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26637  * document.body, make sure you assign an id to the body element.
26638  */
26639 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26640     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26641     this.container = Roo.get(container);
26642 };
26643
26644 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26645     init : function(s){
26646         this.basic.init(s);
26647     },
26648     
26649     getElementSize : function(s){
26650         return this.basic.getElementSize(s);
26651     },
26652     
26653     setElementSize : function(s, newSize, onComplete){
26654         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26655     },
26656     
26657     moveSplitter : function(s){
26658         var yes = Roo.SplitBar;
26659         switch(s.placement){
26660             case yes.LEFT:
26661                 s.el.setX(s.resizingEl.getRight());
26662                 break;
26663             case yes.RIGHT:
26664                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26665                 break;
26666             case yes.TOP:
26667                 s.el.setY(s.resizingEl.getBottom());
26668                 break;
26669             case yes.BOTTOM:
26670                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26671                 break;
26672         }
26673     }
26674 };
26675
26676 /**
26677  * Orientation constant - Create a vertical SplitBar
26678  * @static
26679  * @type Number
26680  */
26681 Roo.SplitBar.VERTICAL = 1;
26682
26683 /**
26684  * Orientation constant - Create a horizontal SplitBar
26685  * @static
26686  * @type Number
26687  */
26688 Roo.SplitBar.HORIZONTAL = 2;
26689
26690 /**
26691  * Placement constant - The resizing element is to the left of the splitter element
26692  * @static
26693  * @type Number
26694  */
26695 Roo.SplitBar.LEFT = 1;
26696
26697 /**
26698  * Placement constant - The resizing element is to the right of the splitter element
26699  * @static
26700  * @type Number
26701  */
26702 Roo.SplitBar.RIGHT = 2;
26703
26704 /**
26705  * Placement constant - The resizing element is positioned above the splitter element
26706  * @static
26707  * @type Number
26708  */
26709 Roo.SplitBar.TOP = 3;
26710
26711 /**
26712  * Placement constant - The resizing element is positioned under splitter element
26713  * @static
26714  * @type Number
26715  */
26716 Roo.SplitBar.BOTTOM = 4;
26717 /*
26718  * Based on:
26719  * Ext JS Library 1.1.1
26720  * Copyright(c) 2006-2007, Ext JS, LLC.
26721  *
26722  * Originally Released Under LGPL - original licence link has changed is not relivant.
26723  *
26724  * Fork - LGPL
26725  * <script type="text/javascript">
26726  */
26727
26728 /**
26729  * @class Roo.View
26730  * @extends Roo.util.Observable
26731  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26732  * This class also supports single and multi selection modes. <br>
26733  * Create a data model bound view:
26734  <pre><code>
26735  var store = new Roo.data.Store(...);
26736
26737  var view = new Roo.View({
26738     el : "my-element",
26739     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26740  
26741     singleSelect: true,
26742     selectedClass: "ydataview-selected",
26743     store: store
26744  });
26745
26746  // listen for node click?
26747  view.on("click", function(vw, index, node, e){
26748  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26749  });
26750
26751  // load XML data
26752  dataModel.load("foobar.xml");
26753  </code></pre>
26754  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26755  * <br><br>
26756  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26757  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26758  * 
26759  * Note: old style constructor is still suported (container, template, config)
26760  * 
26761  * @constructor
26762  * Create a new View
26763  * @param {Object} config The config object
26764  * 
26765  */
26766 Roo.View = function(config, depreciated_tpl, depreciated_config){
26767     
26768     this.parent = false;
26769     
26770     if (typeof(depreciated_tpl) == 'undefined') {
26771         // new way.. - universal constructor.
26772         Roo.apply(this, config);
26773         this.el  = Roo.get(this.el);
26774     } else {
26775         // old format..
26776         this.el  = Roo.get(config);
26777         this.tpl = depreciated_tpl;
26778         Roo.apply(this, depreciated_config);
26779     }
26780     this.wrapEl  = this.el.wrap().wrap();
26781     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26782     
26783     
26784     if(typeof(this.tpl) == "string"){
26785         this.tpl = new Roo.Template(this.tpl);
26786     } else {
26787         // support xtype ctors..
26788         this.tpl = new Roo.factory(this.tpl, Roo);
26789     }
26790     
26791     
26792     this.tpl.compile();
26793     
26794     /** @private */
26795     this.addEvents({
26796         /**
26797          * @event beforeclick
26798          * Fires before a click is processed. Returns false to cancel the default action.
26799          * @param {Roo.View} this
26800          * @param {Number} index The index of the target node
26801          * @param {HTMLElement} node The target node
26802          * @param {Roo.EventObject} e The raw event object
26803          */
26804             "beforeclick" : true,
26805         /**
26806          * @event click
26807          * Fires when a template node is clicked.
26808          * @param {Roo.View} this
26809          * @param {Number} index The index of the target node
26810          * @param {HTMLElement} node The target node
26811          * @param {Roo.EventObject} e The raw event object
26812          */
26813             "click" : true,
26814         /**
26815          * @event dblclick
26816          * Fires when a template node is double clicked.
26817          * @param {Roo.View} this
26818          * @param {Number} index The index of the target node
26819          * @param {HTMLElement} node The target node
26820          * @param {Roo.EventObject} e The raw event object
26821          */
26822             "dblclick" : true,
26823         /**
26824          * @event contextmenu
26825          * Fires when a template node is right clicked.
26826          * @param {Roo.View} this
26827          * @param {Number} index The index of the target node
26828          * @param {HTMLElement} node The target node
26829          * @param {Roo.EventObject} e The raw event object
26830          */
26831             "contextmenu" : true,
26832         /**
26833          * @event selectionchange
26834          * Fires when the selected nodes change.
26835          * @param {Roo.View} this
26836          * @param {Array} selections Array of the selected nodes
26837          */
26838             "selectionchange" : true,
26839     
26840         /**
26841          * @event beforeselect
26842          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26843          * @param {Roo.View} this
26844          * @param {HTMLElement} node The node to be selected
26845          * @param {Array} selections Array of currently selected nodes
26846          */
26847             "beforeselect" : true,
26848         /**
26849          * @event preparedata
26850          * Fires on every row to render, to allow you to change the data.
26851          * @param {Roo.View} this
26852          * @param {Object} data to be rendered (change this)
26853          */
26854           "preparedata" : true
26855           
26856           
26857         });
26858
26859
26860
26861     this.el.on({
26862         "click": this.onClick,
26863         "dblclick": this.onDblClick,
26864         "contextmenu": this.onContextMenu,
26865         scope:this
26866     });
26867
26868     this.selections = [];
26869     this.nodes = [];
26870     this.cmp = new Roo.CompositeElementLite([]);
26871     if(this.store){
26872         this.store = Roo.factory(this.store, Roo.data);
26873         this.setStore(this.store, true);
26874     }
26875     
26876     if ( this.footer && this.footer.xtype) {
26877            
26878          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26879         
26880         this.footer.dataSource = this.store;
26881         this.footer.container = fctr;
26882         this.footer = Roo.factory(this.footer, Roo);
26883         fctr.insertFirst(this.el);
26884         
26885         // this is a bit insane - as the paging toolbar seems to detach the el..
26886 //        dom.parentNode.parentNode.parentNode
26887          // they get detached?
26888     }
26889     
26890     
26891     Roo.View.superclass.constructor.call(this);
26892     
26893     
26894 };
26895
26896 Roo.extend(Roo.View, Roo.util.Observable, {
26897     
26898      /**
26899      * @cfg {Roo.data.Store} store Data store to load data from.
26900      */
26901     store : false,
26902     
26903     /**
26904      * @cfg {String|Roo.Element} el The container element.
26905      */
26906     el : '',
26907     
26908     /**
26909      * @cfg {String|Roo.Template} tpl The template used by this View 
26910      */
26911     tpl : false,
26912     /**
26913      * @cfg {String} dataName the named area of the template to use as the data area
26914      *                          Works with domtemplates roo-name="name"
26915      */
26916     dataName: false,
26917     /**
26918      * @cfg {String} selectedClass The css class to add to selected nodes
26919      */
26920     selectedClass : "x-view-selected",
26921      /**
26922      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26923      */
26924     emptyText : "",
26925     
26926     /**
26927      * @cfg {String} text to display on mask (default Loading)
26928      */
26929     mask : false,
26930     /**
26931      * @cfg {Boolean} multiSelect Allow multiple selection
26932      */
26933     multiSelect : false,
26934     /**
26935      * @cfg {Boolean} singleSelect Allow single selection
26936      */
26937     singleSelect:  false,
26938     
26939     /**
26940      * @cfg {Boolean} toggleSelect - selecting 
26941      */
26942     toggleSelect : false,
26943     
26944     /**
26945      * @cfg {Boolean} tickable - selecting 
26946      */
26947     tickable : false,
26948     
26949     /**
26950      * Returns the element this view is bound to.
26951      * @return {Roo.Element}
26952      */
26953     getEl : function(){
26954         return this.wrapEl;
26955     },
26956     
26957     
26958
26959     /**
26960      * Refreshes the view. - called by datachanged on the store. - do not call directly.
26961      */
26962     refresh : function(){
26963         //Roo.log('refresh');
26964         var t = this.tpl;
26965         
26966         // if we are using something like 'domtemplate', then
26967         // the what gets used is:
26968         // t.applySubtemplate(NAME, data, wrapping data..)
26969         // the outer template then get' applied with
26970         //     the store 'extra data'
26971         // and the body get's added to the
26972         //      roo-name="data" node?
26973         //      <span class='roo-tpl-{name}'></span> ?????
26974         
26975         
26976         
26977         this.clearSelections();
26978         this.el.update("");
26979         var html = [];
26980         var records = this.store.getRange();
26981         if(records.length < 1) {
26982             
26983             // is this valid??  = should it render a template??
26984             
26985             this.el.update(this.emptyText);
26986             return;
26987         }
26988         var el = this.el;
26989         if (this.dataName) {
26990             this.el.update(t.apply(this.store.meta)); //????
26991             el = this.el.child('.roo-tpl-' + this.dataName);
26992         }
26993         
26994         for(var i = 0, len = records.length; i < len; i++){
26995             var data = this.prepareData(records[i].data, i, records[i]);
26996             this.fireEvent("preparedata", this, data, i, records[i]);
26997             
26998             var d = Roo.apply({}, data);
26999             
27000             if(this.tickable){
27001                 Roo.apply(d, {'roo-id' : Roo.id()});
27002                 
27003                 var _this = this;
27004             
27005                 Roo.each(this.parent.item, function(item){
27006                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27007                         return;
27008                     }
27009                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27010                 });
27011             }
27012             
27013             html[html.length] = Roo.util.Format.trim(
27014                 this.dataName ?
27015                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27016                     t.apply(d)
27017             );
27018         }
27019         
27020         
27021         
27022         el.update(html.join(""));
27023         this.nodes = el.dom.childNodes;
27024         this.updateIndexes(0);
27025     },
27026     
27027
27028     /**
27029      * Function to override to reformat the data that is sent to
27030      * the template for each node.
27031      * DEPRICATED - use the preparedata event handler.
27032      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27033      * a JSON object for an UpdateManager bound view).
27034      */
27035     prepareData : function(data, index, record)
27036     {
27037         this.fireEvent("preparedata", this, data, index, record);
27038         return data;
27039     },
27040
27041     onUpdate : function(ds, record){
27042         // Roo.log('on update');   
27043         this.clearSelections();
27044         var index = this.store.indexOf(record);
27045         var n = this.nodes[index];
27046         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27047         n.parentNode.removeChild(n);
27048         this.updateIndexes(index, index);
27049     },
27050
27051     
27052     
27053 // --------- FIXME     
27054     onAdd : function(ds, records, index)
27055     {
27056         //Roo.log(['on Add', ds, records, index] );        
27057         this.clearSelections();
27058         if(this.nodes.length == 0){
27059             this.refresh();
27060             return;
27061         }
27062         var n = this.nodes[index];
27063         for(var i = 0, len = records.length; i < len; i++){
27064             var d = this.prepareData(records[i].data, i, records[i]);
27065             if(n){
27066                 this.tpl.insertBefore(n, d);
27067             }else{
27068                 
27069                 this.tpl.append(this.el, d);
27070             }
27071         }
27072         this.updateIndexes(index);
27073     },
27074
27075     onRemove : function(ds, record, index){
27076        // Roo.log('onRemove');
27077         this.clearSelections();
27078         var el = this.dataName  ?
27079             this.el.child('.roo-tpl-' + this.dataName) :
27080             this.el; 
27081         
27082         el.dom.removeChild(this.nodes[index]);
27083         this.updateIndexes(index);
27084     },
27085
27086     /**
27087      * Refresh an individual node.
27088      * @param {Number} index
27089      */
27090     refreshNode : function(index){
27091         this.onUpdate(this.store, this.store.getAt(index));
27092     },
27093
27094     updateIndexes : function(startIndex, endIndex){
27095         var ns = this.nodes;
27096         startIndex = startIndex || 0;
27097         endIndex = endIndex || ns.length - 1;
27098         for(var i = startIndex; i <= endIndex; i++){
27099             ns[i].nodeIndex = i;
27100         }
27101     },
27102
27103     /**
27104      * Changes the data store this view uses and refresh the view.
27105      * @param {Store} store
27106      */
27107     setStore : function(store, initial){
27108         if(!initial && this.store){
27109             this.store.un("datachanged", this.refresh);
27110             this.store.un("add", this.onAdd);
27111             this.store.un("remove", this.onRemove);
27112             this.store.un("update", this.onUpdate);
27113             this.store.un("clear", this.refresh);
27114             this.store.un("beforeload", this.onBeforeLoad);
27115             this.store.un("load", this.onLoad);
27116             this.store.un("loadexception", this.onLoad);
27117         }
27118         if(store){
27119           
27120             store.on("datachanged", this.refresh, this);
27121             store.on("add", this.onAdd, this);
27122             store.on("remove", this.onRemove, this);
27123             store.on("update", this.onUpdate, this);
27124             store.on("clear", this.refresh, this);
27125             store.on("beforeload", this.onBeforeLoad, this);
27126             store.on("load", this.onLoad, this);
27127             store.on("loadexception", this.onLoad, this);
27128         }
27129         
27130         if(store){
27131             this.refresh();
27132         }
27133     },
27134     /**
27135      * onbeforeLoad - masks the loading area.
27136      *
27137      */
27138     onBeforeLoad : function(store,opts)
27139     {
27140          //Roo.log('onBeforeLoad');   
27141         if (!opts.add) {
27142             this.el.update("");
27143         }
27144         this.el.mask(this.mask ? this.mask : "Loading" ); 
27145     },
27146     onLoad : function ()
27147     {
27148         this.el.unmask();
27149     },
27150     
27151
27152     /**
27153      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27154      * @param {HTMLElement} node
27155      * @return {HTMLElement} The template node
27156      */
27157     findItemFromChild : function(node){
27158         var el = this.dataName  ?
27159             this.el.child('.roo-tpl-' + this.dataName,true) :
27160             this.el.dom; 
27161         
27162         if(!node || node.parentNode == el){
27163                     return node;
27164             }
27165             var p = node.parentNode;
27166             while(p && p != el){
27167             if(p.parentNode == el){
27168                 return p;
27169             }
27170             p = p.parentNode;
27171         }
27172             return null;
27173     },
27174
27175     /** @ignore */
27176     onClick : function(e){
27177         var item = this.findItemFromChild(e.getTarget());
27178         if(item){
27179             var index = this.indexOf(item);
27180             if(this.onItemClick(item, index, e) !== false){
27181                 this.fireEvent("click", this, index, item, e);
27182             }
27183         }else{
27184             this.clearSelections();
27185         }
27186     },
27187
27188     /** @ignore */
27189     onContextMenu : function(e){
27190         var item = this.findItemFromChild(e.getTarget());
27191         if(item){
27192             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27193         }
27194     },
27195
27196     /** @ignore */
27197     onDblClick : function(e){
27198         var item = this.findItemFromChild(e.getTarget());
27199         if(item){
27200             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27201         }
27202     },
27203
27204     onItemClick : function(item, index, e)
27205     {
27206         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27207             return false;
27208         }
27209         if (this.toggleSelect) {
27210             var m = this.isSelected(item) ? 'unselect' : 'select';
27211             //Roo.log(m);
27212             var _t = this;
27213             _t[m](item, true, false);
27214             return true;
27215         }
27216         if(this.multiSelect || this.singleSelect){
27217             if(this.multiSelect && e.shiftKey && this.lastSelection){
27218                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27219             }else{
27220                 this.select(item, this.multiSelect && e.ctrlKey);
27221                 this.lastSelection = item;
27222             }
27223             
27224             if(!this.tickable){
27225                 e.preventDefault();
27226             }
27227             
27228         }
27229         return true;
27230     },
27231
27232     /**
27233      * Get the number of selected nodes.
27234      * @return {Number}
27235      */
27236     getSelectionCount : function(){
27237         return this.selections.length;
27238     },
27239
27240     /**
27241      * Get the currently selected nodes.
27242      * @return {Array} An array of HTMLElements
27243      */
27244     getSelectedNodes : function(){
27245         return this.selections;
27246     },
27247
27248     /**
27249      * Get the indexes of the selected nodes.
27250      * @return {Array}
27251      */
27252     getSelectedIndexes : function(){
27253         var indexes = [], s = this.selections;
27254         for(var i = 0, len = s.length; i < len; i++){
27255             indexes.push(s[i].nodeIndex);
27256         }
27257         return indexes;
27258     },
27259
27260     /**
27261      * Clear all selections
27262      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27263      */
27264     clearSelections : function(suppressEvent){
27265         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27266             this.cmp.elements = this.selections;
27267             this.cmp.removeClass(this.selectedClass);
27268             this.selections = [];
27269             if(!suppressEvent){
27270                 this.fireEvent("selectionchange", this, this.selections);
27271             }
27272         }
27273     },
27274
27275     /**
27276      * Returns true if the passed node is selected
27277      * @param {HTMLElement/Number} node The node or node index
27278      * @return {Boolean}
27279      */
27280     isSelected : function(node){
27281         var s = this.selections;
27282         if(s.length < 1){
27283             return false;
27284         }
27285         node = this.getNode(node);
27286         return s.indexOf(node) !== -1;
27287     },
27288
27289     /**
27290      * Selects nodes.
27291      * @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
27292      * @param {Boolean} keepExisting (optional) true to keep existing selections
27293      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27294      */
27295     select : function(nodeInfo, keepExisting, suppressEvent){
27296         if(nodeInfo instanceof Array){
27297             if(!keepExisting){
27298                 this.clearSelections(true);
27299             }
27300             for(var i = 0, len = nodeInfo.length; i < len; i++){
27301                 this.select(nodeInfo[i], true, true);
27302             }
27303             return;
27304         } 
27305         var node = this.getNode(nodeInfo);
27306         if(!node || this.isSelected(node)){
27307             return; // already selected.
27308         }
27309         if(!keepExisting){
27310             this.clearSelections(true);
27311         }
27312         
27313         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27314             Roo.fly(node).addClass(this.selectedClass);
27315             this.selections.push(node);
27316             if(!suppressEvent){
27317                 this.fireEvent("selectionchange", this, this.selections);
27318             }
27319         }
27320         
27321         
27322     },
27323       /**
27324      * Unselects nodes.
27325      * @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
27326      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27327      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27328      */
27329     unselect : function(nodeInfo, keepExisting, suppressEvent)
27330     {
27331         if(nodeInfo instanceof Array){
27332             Roo.each(this.selections, function(s) {
27333                 this.unselect(s, nodeInfo);
27334             }, this);
27335             return;
27336         }
27337         var node = this.getNode(nodeInfo);
27338         if(!node || !this.isSelected(node)){
27339             //Roo.log("not selected");
27340             return; // not selected.
27341         }
27342         // fireevent???
27343         var ns = [];
27344         Roo.each(this.selections, function(s) {
27345             if (s == node ) {
27346                 Roo.fly(node).removeClass(this.selectedClass);
27347
27348                 return;
27349             }
27350             ns.push(s);
27351         },this);
27352         
27353         this.selections= ns;
27354         this.fireEvent("selectionchange", this, this.selections);
27355     },
27356
27357     /**
27358      * Gets a template node.
27359      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27360      * @return {HTMLElement} The node or null if it wasn't found
27361      */
27362     getNode : function(nodeInfo){
27363         if(typeof nodeInfo == "string"){
27364             return document.getElementById(nodeInfo);
27365         }else if(typeof nodeInfo == "number"){
27366             return this.nodes[nodeInfo];
27367         }
27368         return nodeInfo;
27369     },
27370
27371     /**
27372      * Gets a range template nodes.
27373      * @param {Number} startIndex
27374      * @param {Number} endIndex
27375      * @return {Array} An array of nodes
27376      */
27377     getNodes : function(start, end){
27378         var ns = this.nodes;
27379         start = start || 0;
27380         end = typeof end == "undefined" ? ns.length - 1 : end;
27381         var nodes = [];
27382         if(start <= end){
27383             for(var i = start; i <= end; i++){
27384                 nodes.push(ns[i]);
27385             }
27386         } else{
27387             for(var i = start; i >= end; i--){
27388                 nodes.push(ns[i]);
27389             }
27390         }
27391         return nodes;
27392     },
27393
27394     /**
27395      * Finds the index of the passed node
27396      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27397      * @return {Number} The index of the node or -1
27398      */
27399     indexOf : function(node){
27400         node = this.getNode(node);
27401         if(typeof node.nodeIndex == "number"){
27402             return node.nodeIndex;
27403         }
27404         var ns = this.nodes;
27405         for(var i = 0, len = ns.length; i < len; i++){
27406             if(ns[i] == node){
27407                 return i;
27408             }
27409         }
27410         return -1;
27411     }
27412 });
27413 /*
27414  * Based on:
27415  * Ext JS Library 1.1.1
27416  * Copyright(c) 2006-2007, Ext JS, LLC.
27417  *
27418  * Originally Released Under LGPL - original licence link has changed is not relivant.
27419  *
27420  * Fork - LGPL
27421  * <script type="text/javascript">
27422  */
27423
27424 /**
27425  * @class Roo.JsonView
27426  * @extends Roo.View
27427  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27428 <pre><code>
27429 var view = new Roo.JsonView({
27430     container: "my-element",
27431     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27432     multiSelect: true, 
27433     jsonRoot: "data" 
27434 });
27435
27436 // listen for node click?
27437 view.on("click", function(vw, index, node, e){
27438     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27439 });
27440
27441 // direct load of JSON data
27442 view.load("foobar.php");
27443
27444 // Example from my blog list
27445 var tpl = new Roo.Template(
27446     '&lt;div class="entry"&gt;' +
27447     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27448     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27449     "&lt;/div&gt;&lt;hr /&gt;"
27450 );
27451
27452 var moreView = new Roo.JsonView({
27453     container :  "entry-list", 
27454     template : tpl,
27455     jsonRoot: "posts"
27456 });
27457 moreView.on("beforerender", this.sortEntries, this);
27458 moreView.load({
27459     url: "/blog/get-posts.php",
27460     params: "allposts=true",
27461     text: "Loading Blog Entries..."
27462 });
27463 </code></pre>
27464
27465 * Note: old code is supported with arguments : (container, template, config)
27466
27467
27468  * @constructor
27469  * Create a new JsonView
27470  * 
27471  * @param {Object} config The config object
27472  * 
27473  */
27474 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27475     
27476     
27477     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27478
27479     var um = this.el.getUpdateManager();
27480     um.setRenderer(this);
27481     um.on("update", this.onLoad, this);
27482     um.on("failure", this.onLoadException, this);
27483
27484     /**
27485      * @event beforerender
27486      * Fires before rendering of the downloaded JSON data.
27487      * @param {Roo.JsonView} this
27488      * @param {Object} data The JSON data loaded
27489      */
27490     /**
27491      * @event load
27492      * Fires when data is loaded.
27493      * @param {Roo.JsonView} this
27494      * @param {Object} data The JSON data loaded
27495      * @param {Object} response The raw Connect response object
27496      */
27497     /**
27498      * @event loadexception
27499      * Fires when loading fails.
27500      * @param {Roo.JsonView} this
27501      * @param {Object} response The raw Connect response object
27502      */
27503     this.addEvents({
27504         'beforerender' : true,
27505         'load' : true,
27506         'loadexception' : true
27507     });
27508 };
27509 Roo.extend(Roo.JsonView, Roo.View, {
27510     /**
27511      * @type {String} The root property in the loaded JSON object that contains the data
27512      */
27513     jsonRoot : "",
27514
27515     /**
27516      * Refreshes the view.
27517      */
27518     refresh : function(){
27519         this.clearSelections();
27520         this.el.update("");
27521         var html = [];
27522         var o = this.jsonData;
27523         if(o && o.length > 0){
27524             for(var i = 0, len = o.length; i < len; i++){
27525                 var data = this.prepareData(o[i], i, o);
27526                 html[html.length] = this.tpl.apply(data);
27527             }
27528         }else{
27529             html.push(this.emptyText);
27530         }
27531         this.el.update(html.join(""));
27532         this.nodes = this.el.dom.childNodes;
27533         this.updateIndexes(0);
27534     },
27535
27536     /**
27537      * 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.
27538      * @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:
27539      <pre><code>
27540      view.load({
27541          url: "your-url.php",
27542          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27543          callback: yourFunction,
27544          scope: yourObject, //(optional scope)
27545          discardUrl: false,
27546          nocache: false,
27547          text: "Loading...",
27548          timeout: 30,
27549          scripts: false
27550      });
27551      </code></pre>
27552      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27553      * 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.
27554      * @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}
27555      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27556      * @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.
27557      */
27558     load : function(){
27559         var um = this.el.getUpdateManager();
27560         um.update.apply(um, arguments);
27561     },
27562
27563     // note - render is a standard framework call...
27564     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27565     render : function(el, response){
27566         
27567         this.clearSelections();
27568         this.el.update("");
27569         var o;
27570         try{
27571             if (response != '') {
27572                 o = Roo.util.JSON.decode(response.responseText);
27573                 if(this.jsonRoot){
27574                     
27575                     o = o[this.jsonRoot];
27576                 }
27577             }
27578         } catch(e){
27579         }
27580         /**
27581          * The current JSON data or null
27582          */
27583         this.jsonData = o;
27584         this.beforeRender();
27585         this.refresh();
27586     },
27587
27588 /**
27589  * Get the number of records in the current JSON dataset
27590  * @return {Number}
27591  */
27592     getCount : function(){
27593         return this.jsonData ? this.jsonData.length : 0;
27594     },
27595
27596 /**
27597  * Returns the JSON object for the specified node(s)
27598  * @param {HTMLElement/Array} node The node or an array of nodes
27599  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27600  * you get the JSON object for the node
27601  */
27602     getNodeData : function(node){
27603         if(node instanceof Array){
27604             var data = [];
27605             for(var i = 0, len = node.length; i < len; i++){
27606                 data.push(this.getNodeData(node[i]));
27607             }
27608             return data;
27609         }
27610         return this.jsonData[this.indexOf(node)] || null;
27611     },
27612
27613     beforeRender : function(){
27614         this.snapshot = this.jsonData;
27615         if(this.sortInfo){
27616             this.sort.apply(this, this.sortInfo);
27617         }
27618         this.fireEvent("beforerender", this, this.jsonData);
27619     },
27620
27621     onLoad : function(el, o){
27622         this.fireEvent("load", this, this.jsonData, o);
27623     },
27624
27625     onLoadException : function(el, o){
27626         this.fireEvent("loadexception", this, o);
27627     },
27628
27629 /**
27630  * Filter the data by a specific property.
27631  * @param {String} property A property on your JSON objects
27632  * @param {String/RegExp} value Either string that the property values
27633  * should start with, or a RegExp to test against the property
27634  */
27635     filter : function(property, value){
27636         if(this.jsonData){
27637             var data = [];
27638             var ss = this.snapshot;
27639             if(typeof value == "string"){
27640                 var vlen = value.length;
27641                 if(vlen == 0){
27642                     this.clearFilter();
27643                     return;
27644                 }
27645                 value = value.toLowerCase();
27646                 for(var i = 0, len = ss.length; i < len; i++){
27647                     var o = ss[i];
27648                     if(o[property].substr(0, vlen).toLowerCase() == value){
27649                         data.push(o);
27650                     }
27651                 }
27652             } else if(value.exec){ // regex?
27653                 for(var i = 0, len = ss.length; i < len; i++){
27654                     var o = ss[i];
27655                     if(value.test(o[property])){
27656                         data.push(o);
27657                     }
27658                 }
27659             } else{
27660                 return;
27661             }
27662             this.jsonData = data;
27663             this.refresh();
27664         }
27665     },
27666
27667 /**
27668  * Filter by a function. The passed function will be called with each
27669  * object in the current dataset. If the function returns true the value is kept,
27670  * otherwise it is filtered.
27671  * @param {Function} fn
27672  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27673  */
27674     filterBy : function(fn, scope){
27675         if(this.jsonData){
27676             var data = [];
27677             var ss = this.snapshot;
27678             for(var i = 0, len = ss.length; i < len; i++){
27679                 var o = ss[i];
27680                 if(fn.call(scope || this, o)){
27681                     data.push(o);
27682                 }
27683             }
27684             this.jsonData = data;
27685             this.refresh();
27686         }
27687     },
27688
27689 /**
27690  * Clears the current filter.
27691  */
27692     clearFilter : function(){
27693         if(this.snapshot && this.jsonData != this.snapshot){
27694             this.jsonData = this.snapshot;
27695             this.refresh();
27696         }
27697     },
27698
27699
27700 /**
27701  * Sorts the data for this view and refreshes it.
27702  * @param {String} property A property on your JSON objects to sort on
27703  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27704  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27705  */
27706     sort : function(property, dir, sortType){
27707         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27708         if(this.jsonData){
27709             var p = property;
27710             var dsc = dir && dir.toLowerCase() == "desc";
27711             var f = function(o1, o2){
27712                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27713                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27714                 ;
27715                 if(v1 < v2){
27716                     return dsc ? +1 : -1;
27717                 } else if(v1 > v2){
27718                     return dsc ? -1 : +1;
27719                 } else{
27720                     return 0;
27721                 }
27722             };
27723             this.jsonData.sort(f);
27724             this.refresh();
27725             if(this.jsonData != this.snapshot){
27726                 this.snapshot.sort(f);
27727             }
27728         }
27729     }
27730 });/*
27731  * Based on:
27732  * Ext JS Library 1.1.1
27733  * Copyright(c) 2006-2007, Ext JS, LLC.
27734  *
27735  * Originally Released Under LGPL - original licence link has changed is not relivant.
27736  *
27737  * Fork - LGPL
27738  * <script type="text/javascript">
27739  */
27740  
27741
27742 /**
27743  * @class Roo.ColorPalette
27744  * @extends Roo.Component
27745  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27746  * Here's an example of typical usage:
27747  * <pre><code>
27748 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27749 cp.render('my-div');
27750
27751 cp.on('select', function(palette, selColor){
27752     // do something with selColor
27753 });
27754 </code></pre>
27755  * @constructor
27756  * Create a new ColorPalette
27757  * @param {Object} config The config object
27758  */
27759 Roo.ColorPalette = function(config){
27760     Roo.ColorPalette.superclass.constructor.call(this, config);
27761     this.addEvents({
27762         /**
27763              * @event select
27764              * Fires when a color is selected
27765              * @param {ColorPalette} this
27766              * @param {String} color The 6-digit color hex code (without the # symbol)
27767              */
27768         select: true
27769     });
27770
27771     if(this.handler){
27772         this.on("select", this.handler, this.scope, true);
27773     }
27774 };
27775 Roo.extend(Roo.ColorPalette, Roo.Component, {
27776     /**
27777      * @cfg {String} itemCls
27778      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27779      */
27780     itemCls : "x-color-palette",
27781     /**
27782      * @cfg {String} value
27783      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27784      * the hex codes are case-sensitive.
27785      */
27786     value : null,
27787     clickEvent:'click',
27788     // private
27789     ctype: "Roo.ColorPalette",
27790
27791     /**
27792      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27793      */
27794     allowReselect : false,
27795
27796     /**
27797      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27798      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27799      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27800      * of colors with the width setting until the box is symmetrical.</p>
27801      * <p>You can override individual colors if needed:</p>
27802      * <pre><code>
27803 var cp = new Roo.ColorPalette();
27804 cp.colors[0] = "FF0000";  // change the first box to red
27805 </code></pre>
27806
27807 Or you can provide a custom array of your own for complete control:
27808 <pre><code>
27809 var cp = new Roo.ColorPalette();
27810 cp.colors = ["000000", "993300", "333300"];
27811 </code></pre>
27812      * @type Array
27813      */
27814     colors : [
27815         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27816         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27817         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27818         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27819         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27820     ],
27821
27822     // private
27823     onRender : function(container, position){
27824         var t = new Roo.MasterTemplate(
27825             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27826         );
27827         var c = this.colors;
27828         for(var i = 0, len = c.length; i < len; i++){
27829             t.add([c[i]]);
27830         }
27831         var el = document.createElement("div");
27832         el.className = this.itemCls;
27833         t.overwrite(el);
27834         container.dom.insertBefore(el, position);
27835         this.el = Roo.get(el);
27836         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27837         if(this.clickEvent != 'click'){
27838             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27839         }
27840     },
27841
27842     // private
27843     afterRender : function(){
27844         Roo.ColorPalette.superclass.afterRender.call(this);
27845         if(this.value){
27846             var s = this.value;
27847             this.value = null;
27848             this.select(s);
27849         }
27850     },
27851
27852     // private
27853     handleClick : function(e, t){
27854         e.preventDefault();
27855         if(!this.disabled){
27856             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27857             this.select(c.toUpperCase());
27858         }
27859     },
27860
27861     /**
27862      * Selects the specified color in the palette (fires the select event)
27863      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27864      */
27865     select : function(color){
27866         color = color.replace("#", "");
27867         if(color != this.value || this.allowReselect){
27868             var el = this.el;
27869             if(this.value){
27870                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27871             }
27872             el.child("a.color-"+color).addClass("x-color-palette-sel");
27873             this.value = color;
27874             this.fireEvent("select", this, color);
27875         }
27876     }
27877 });/*
27878  * Based on:
27879  * Ext JS Library 1.1.1
27880  * Copyright(c) 2006-2007, Ext JS, LLC.
27881  *
27882  * Originally Released Under LGPL - original licence link has changed is not relivant.
27883  *
27884  * Fork - LGPL
27885  * <script type="text/javascript">
27886  */
27887  
27888 /**
27889  * @class Roo.DatePicker
27890  * @extends Roo.Component
27891  * Simple date picker class.
27892  * @constructor
27893  * Create a new DatePicker
27894  * @param {Object} config The config object
27895  */
27896 Roo.DatePicker = function(config){
27897     Roo.DatePicker.superclass.constructor.call(this, config);
27898
27899     this.value = config && config.value ?
27900                  config.value.clearTime() : new Date().clearTime();
27901
27902     this.addEvents({
27903         /**
27904              * @event select
27905              * Fires when a date is selected
27906              * @param {DatePicker} this
27907              * @param {Date} date The selected date
27908              */
27909         'select': true,
27910         /**
27911              * @event monthchange
27912              * Fires when the displayed month changes 
27913              * @param {DatePicker} this
27914              * @param {Date} date The selected month
27915              */
27916         'monthchange': true
27917     });
27918
27919     if(this.handler){
27920         this.on("select", this.handler,  this.scope || this);
27921     }
27922     // build the disabledDatesRE
27923     if(!this.disabledDatesRE && this.disabledDates){
27924         var dd = this.disabledDates;
27925         var re = "(?:";
27926         for(var i = 0; i < dd.length; i++){
27927             re += dd[i];
27928             if(i != dd.length-1) {
27929                 re += "|";
27930             }
27931         }
27932         this.disabledDatesRE = new RegExp(re + ")");
27933     }
27934 };
27935
27936 Roo.extend(Roo.DatePicker, Roo.Component, {
27937     /**
27938      * @cfg {String} todayText
27939      * The text to display on the button that selects the current date (defaults to "Today")
27940      */
27941     todayText : "Today",
27942     /**
27943      * @cfg {String} okText
27944      * The text to display on the ok button
27945      */
27946     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
27947     /**
27948      * @cfg {String} cancelText
27949      * The text to display on the cancel button
27950      */
27951     cancelText : "Cancel",
27952     /**
27953      * @cfg {String} todayTip
27954      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
27955      */
27956     todayTip : "{0} (Spacebar)",
27957     /**
27958      * @cfg {Date} minDate
27959      * Minimum allowable date (JavaScript date object, defaults to null)
27960      */
27961     minDate : null,
27962     /**
27963      * @cfg {Date} maxDate
27964      * Maximum allowable date (JavaScript date object, defaults to null)
27965      */
27966     maxDate : null,
27967     /**
27968      * @cfg {String} minText
27969      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
27970      */
27971     minText : "This date is before the minimum date",
27972     /**
27973      * @cfg {String} maxText
27974      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
27975      */
27976     maxText : "This date is after the maximum date",
27977     /**
27978      * @cfg {String} format
27979      * The default date format string which can be overriden for localization support.  The format must be
27980      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
27981      */
27982     format : "m/d/y",
27983     /**
27984      * @cfg {Array} disabledDays
27985      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
27986      */
27987     disabledDays : null,
27988     /**
27989      * @cfg {String} disabledDaysText
27990      * The tooltip to display when the date falls on a disabled day (defaults to "")
27991      */
27992     disabledDaysText : "",
27993     /**
27994      * @cfg {RegExp} disabledDatesRE
27995      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
27996      */
27997     disabledDatesRE : null,
27998     /**
27999      * @cfg {String} disabledDatesText
28000      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28001      */
28002     disabledDatesText : "",
28003     /**
28004      * @cfg {Boolean} constrainToViewport
28005      * True to constrain the date picker to the viewport (defaults to true)
28006      */
28007     constrainToViewport : true,
28008     /**
28009      * @cfg {Array} monthNames
28010      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28011      */
28012     monthNames : Date.monthNames,
28013     /**
28014      * @cfg {Array} dayNames
28015      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28016      */
28017     dayNames : Date.dayNames,
28018     /**
28019      * @cfg {String} nextText
28020      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28021      */
28022     nextText: 'Next Month (Control+Right)',
28023     /**
28024      * @cfg {String} prevText
28025      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28026      */
28027     prevText: 'Previous Month (Control+Left)',
28028     /**
28029      * @cfg {String} monthYearText
28030      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28031      */
28032     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28033     /**
28034      * @cfg {Number} startDay
28035      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28036      */
28037     startDay : 0,
28038     /**
28039      * @cfg {Bool} showClear
28040      * Show a clear button (usefull for date form elements that can be blank.)
28041      */
28042     
28043     showClear: false,
28044     
28045     /**
28046      * Sets the value of the date field
28047      * @param {Date} value The date to set
28048      */
28049     setValue : function(value){
28050         var old = this.value;
28051         
28052         if (typeof(value) == 'string') {
28053          
28054             value = Date.parseDate(value, this.format);
28055         }
28056         if (!value) {
28057             value = new Date();
28058         }
28059         
28060         this.value = value.clearTime(true);
28061         if(this.el){
28062             this.update(this.value);
28063         }
28064     },
28065
28066     /**
28067      * Gets the current selected value of the date field
28068      * @return {Date} The selected date
28069      */
28070     getValue : function(){
28071         return this.value;
28072     },
28073
28074     // private
28075     focus : function(){
28076         if(this.el){
28077             this.update(this.activeDate);
28078         }
28079     },
28080
28081     // privateval
28082     onRender : function(container, position){
28083         
28084         var m = [
28085              '<table cellspacing="0">',
28086                 '<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>',
28087                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28088         var dn = this.dayNames;
28089         for(var i = 0; i < 7; i++){
28090             var d = this.startDay+i;
28091             if(d > 6){
28092                 d = d-7;
28093             }
28094             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28095         }
28096         m[m.length] = "</tr></thead><tbody><tr>";
28097         for(var i = 0; i < 42; i++) {
28098             if(i % 7 == 0 && i != 0){
28099                 m[m.length] = "</tr><tr>";
28100             }
28101             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28102         }
28103         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28104             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28105
28106         var el = document.createElement("div");
28107         el.className = "x-date-picker";
28108         el.innerHTML = m.join("");
28109
28110         container.dom.insertBefore(el, position);
28111
28112         this.el = Roo.get(el);
28113         this.eventEl = Roo.get(el.firstChild);
28114
28115         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28116             handler: this.showPrevMonth,
28117             scope: this,
28118             preventDefault:true,
28119             stopDefault:true
28120         });
28121
28122         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28123             handler: this.showNextMonth,
28124             scope: this,
28125             preventDefault:true,
28126             stopDefault:true
28127         });
28128
28129         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28130
28131         this.monthPicker = this.el.down('div.x-date-mp');
28132         this.monthPicker.enableDisplayMode('block');
28133         
28134         var kn = new Roo.KeyNav(this.eventEl, {
28135             "left" : function(e){
28136                 e.ctrlKey ?
28137                     this.showPrevMonth() :
28138                     this.update(this.activeDate.add("d", -1));
28139             },
28140
28141             "right" : function(e){
28142                 e.ctrlKey ?
28143                     this.showNextMonth() :
28144                     this.update(this.activeDate.add("d", 1));
28145             },
28146
28147             "up" : function(e){
28148                 e.ctrlKey ?
28149                     this.showNextYear() :
28150                     this.update(this.activeDate.add("d", -7));
28151             },
28152
28153             "down" : function(e){
28154                 e.ctrlKey ?
28155                     this.showPrevYear() :
28156                     this.update(this.activeDate.add("d", 7));
28157             },
28158
28159             "pageUp" : function(e){
28160                 this.showNextMonth();
28161             },
28162
28163             "pageDown" : function(e){
28164                 this.showPrevMonth();
28165             },
28166
28167             "enter" : function(e){
28168                 e.stopPropagation();
28169                 return true;
28170             },
28171
28172             scope : this
28173         });
28174
28175         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28176
28177         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28178
28179         this.el.unselectable();
28180         
28181         this.cells = this.el.select("table.x-date-inner tbody td");
28182         this.textNodes = this.el.query("table.x-date-inner tbody span");
28183
28184         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28185             text: "&#160;",
28186             tooltip: this.monthYearText
28187         });
28188
28189         this.mbtn.on('click', this.showMonthPicker, this);
28190         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28191
28192
28193         var today = (new Date()).dateFormat(this.format);
28194         
28195         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28196         if (this.showClear) {
28197             baseTb.add( new Roo.Toolbar.Fill());
28198         }
28199         baseTb.add({
28200             text: String.format(this.todayText, today),
28201             tooltip: String.format(this.todayTip, today),
28202             handler: this.selectToday,
28203             scope: this
28204         });
28205         
28206         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28207             
28208         //});
28209         if (this.showClear) {
28210             
28211             baseTb.add( new Roo.Toolbar.Fill());
28212             baseTb.add({
28213                 text: '&#160;',
28214                 cls: 'x-btn-icon x-btn-clear',
28215                 handler: function() {
28216                     //this.value = '';
28217                     this.fireEvent("select", this, '');
28218                 },
28219                 scope: this
28220             });
28221         }
28222         
28223         
28224         if(Roo.isIE){
28225             this.el.repaint();
28226         }
28227         this.update(this.value);
28228     },
28229
28230     createMonthPicker : function(){
28231         if(!this.monthPicker.dom.firstChild){
28232             var buf = ['<table border="0" cellspacing="0">'];
28233             for(var i = 0; i < 6; i++){
28234                 buf.push(
28235                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28236                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28237                     i == 0 ?
28238                     '<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>' :
28239                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28240                 );
28241             }
28242             buf.push(
28243                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28244                     this.okText,
28245                     '</button><button type="button" class="x-date-mp-cancel">',
28246                     this.cancelText,
28247                     '</button></td></tr>',
28248                 '</table>'
28249             );
28250             this.monthPicker.update(buf.join(''));
28251             this.monthPicker.on('click', this.onMonthClick, this);
28252             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28253
28254             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28255             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28256
28257             this.mpMonths.each(function(m, a, i){
28258                 i += 1;
28259                 if((i%2) == 0){
28260                     m.dom.xmonth = 5 + Math.round(i * .5);
28261                 }else{
28262                     m.dom.xmonth = Math.round((i-1) * .5);
28263                 }
28264             });
28265         }
28266     },
28267
28268     showMonthPicker : function(){
28269         this.createMonthPicker();
28270         var size = this.el.getSize();
28271         this.monthPicker.setSize(size);
28272         this.monthPicker.child('table').setSize(size);
28273
28274         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28275         this.updateMPMonth(this.mpSelMonth);
28276         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28277         this.updateMPYear(this.mpSelYear);
28278
28279         this.monthPicker.slideIn('t', {duration:.2});
28280     },
28281
28282     updateMPYear : function(y){
28283         this.mpyear = y;
28284         var ys = this.mpYears.elements;
28285         for(var i = 1; i <= 10; i++){
28286             var td = ys[i-1], y2;
28287             if((i%2) == 0){
28288                 y2 = y + Math.round(i * .5);
28289                 td.firstChild.innerHTML = y2;
28290                 td.xyear = y2;
28291             }else{
28292                 y2 = y - (5-Math.round(i * .5));
28293                 td.firstChild.innerHTML = y2;
28294                 td.xyear = y2;
28295             }
28296             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28297         }
28298     },
28299
28300     updateMPMonth : function(sm){
28301         this.mpMonths.each(function(m, a, i){
28302             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28303         });
28304     },
28305
28306     selectMPMonth: function(m){
28307         
28308     },
28309
28310     onMonthClick : function(e, t){
28311         e.stopEvent();
28312         var el = new Roo.Element(t), pn;
28313         if(el.is('button.x-date-mp-cancel')){
28314             this.hideMonthPicker();
28315         }
28316         else if(el.is('button.x-date-mp-ok')){
28317             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28318             this.hideMonthPicker();
28319         }
28320         else if(pn = el.up('td.x-date-mp-month', 2)){
28321             this.mpMonths.removeClass('x-date-mp-sel');
28322             pn.addClass('x-date-mp-sel');
28323             this.mpSelMonth = pn.dom.xmonth;
28324         }
28325         else if(pn = el.up('td.x-date-mp-year', 2)){
28326             this.mpYears.removeClass('x-date-mp-sel');
28327             pn.addClass('x-date-mp-sel');
28328             this.mpSelYear = pn.dom.xyear;
28329         }
28330         else if(el.is('a.x-date-mp-prev')){
28331             this.updateMPYear(this.mpyear-10);
28332         }
28333         else if(el.is('a.x-date-mp-next')){
28334             this.updateMPYear(this.mpyear+10);
28335         }
28336     },
28337
28338     onMonthDblClick : function(e, t){
28339         e.stopEvent();
28340         var el = new Roo.Element(t), pn;
28341         if(pn = el.up('td.x-date-mp-month', 2)){
28342             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28343             this.hideMonthPicker();
28344         }
28345         else if(pn = el.up('td.x-date-mp-year', 2)){
28346             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28347             this.hideMonthPicker();
28348         }
28349     },
28350
28351     hideMonthPicker : function(disableAnim){
28352         if(this.monthPicker){
28353             if(disableAnim === true){
28354                 this.monthPicker.hide();
28355             }else{
28356                 this.monthPicker.slideOut('t', {duration:.2});
28357             }
28358         }
28359     },
28360
28361     // private
28362     showPrevMonth : function(e){
28363         this.update(this.activeDate.add("mo", -1));
28364     },
28365
28366     // private
28367     showNextMonth : function(e){
28368         this.update(this.activeDate.add("mo", 1));
28369     },
28370
28371     // private
28372     showPrevYear : function(){
28373         this.update(this.activeDate.add("y", -1));
28374     },
28375
28376     // private
28377     showNextYear : function(){
28378         this.update(this.activeDate.add("y", 1));
28379     },
28380
28381     // private
28382     handleMouseWheel : function(e){
28383         var delta = e.getWheelDelta();
28384         if(delta > 0){
28385             this.showPrevMonth();
28386             e.stopEvent();
28387         } else if(delta < 0){
28388             this.showNextMonth();
28389             e.stopEvent();
28390         }
28391     },
28392
28393     // private
28394     handleDateClick : function(e, t){
28395         e.stopEvent();
28396         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28397             this.setValue(new Date(t.dateValue));
28398             this.fireEvent("select", this, this.value);
28399         }
28400     },
28401
28402     // private
28403     selectToday : function(){
28404         this.setValue(new Date().clearTime());
28405         this.fireEvent("select", this, this.value);
28406     },
28407
28408     // private
28409     update : function(date)
28410     {
28411         var vd = this.activeDate;
28412         this.activeDate = date;
28413         if(vd && this.el){
28414             var t = date.getTime();
28415             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28416                 this.cells.removeClass("x-date-selected");
28417                 this.cells.each(function(c){
28418                    if(c.dom.firstChild.dateValue == t){
28419                        c.addClass("x-date-selected");
28420                        setTimeout(function(){
28421                             try{c.dom.firstChild.focus();}catch(e){}
28422                        }, 50);
28423                        return false;
28424                    }
28425                 });
28426                 return;
28427             }
28428         }
28429         
28430         var days = date.getDaysInMonth();
28431         var firstOfMonth = date.getFirstDateOfMonth();
28432         var startingPos = firstOfMonth.getDay()-this.startDay;
28433
28434         if(startingPos <= this.startDay){
28435             startingPos += 7;
28436         }
28437
28438         var pm = date.add("mo", -1);
28439         var prevStart = pm.getDaysInMonth()-startingPos;
28440
28441         var cells = this.cells.elements;
28442         var textEls = this.textNodes;
28443         days += startingPos;
28444
28445         // convert everything to numbers so it's fast
28446         var day = 86400000;
28447         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28448         var today = new Date().clearTime().getTime();
28449         var sel = date.clearTime().getTime();
28450         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28451         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28452         var ddMatch = this.disabledDatesRE;
28453         var ddText = this.disabledDatesText;
28454         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28455         var ddaysText = this.disabledDaysText;
28456         var format = this.format;
28457
28458         var setCellClass = function(cal, cell){
28459             cell.title = "";
28460             var t = d.getTime();
28461             cell.firstChild.dateValue = t;
28462             if(t == today){
28463                 cell.className += " x-date-today";
28464                 cell.title = cal.todayText;
28465             }
28466             if(t == sel){
28467                 cell.className += " x-date-selected";
28468                 setTimeout(function(){
28469                     try{cell.firstChild.focus();}catch(e){}
28470                 }, 50);
28471             }
28472             // disabling
28473             if(t < min) {
28474                 cell.className = " x-date-disabled";
28475                 cell.title = cal.minText;
28476                 return;
28477             }
28478             if(t > max) {
28479                 cell.className = " x-date-disabled";
28480                 cell.title = cal.maxText;
28481                 return;
28482             }
28483             if(ddays){
28484                 if(ddays.indexOf(d.getDay()) != -1){
28485                     cell.title = ddaysText;
28486                     cell.className = " x-date-disabled";
28487                 }
28488             }
28489             if(ddMatch && format){
28490                 var fvalue = d.dateFormat(format);
28491                 if(ddMatch.test(fvalue)){
28492                     cell.title = ddText.replace("%0", fvalue);
28493                     cell.className = " x-date-disabled";
28494                 }
28495             }
28496         };
28497
28498         var i = 0;
28499         for(; i < startingPos; i++) {
28500             textEls[i].innerHTML = (++prevStart);
28501             d.setDate(d.getDate()+1);
28502             cells[i].className = "x-date-prevday";
28503             setCellClass(this, cells[i]);
28504         }
28505         for(; i < days; i++){
28506             intDay = i - startingPos + 1;
28507             textEls[i].innerHTML = (intDay);
28508             d.setDate(d.getDate()+1);
28509             cells[i].className = "x-date-active";
28510             setCellClass(this, cells[i]);
28511         }
28512         var extraDays = 0;
28513         for(; i < 42; i++) {
28514              textEls[i].innerHTML = (++extraDays);
28515              d.setDate(d.getDate()+1);
28516              cells[i].className = "x-date-nextday";
28517              setCellClass(this, cells[i]);
28518         }
28519
28520         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28521         this.fireEvent('monthchange', this, date);
28522         
28523         if(!this.internalRender){
28524             var main = this.el.dom.firstChild;
28525             var w = main.offsetWidth;
28526             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28527             Roo.fly(main).setWidth(w);
28528             this.internalRender = true;
28529             // opera does not respect the auto grow header center column
28530             // then, after it gets a width opera refuses to recalculate
28531             // without a second pass
28532             if(Roo.isOpera && !this.secondPass){
28533                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28534                 this.secondPass = true;
28535                 this.update.defer(10, this, [date]);
28536             }
28537         }
28538         
28539         
28540     }
28541 });        /*
28542  * Based on:
28543  * Ext JS Library 1.1.1
28544  * Copyright(c) 2006-2007, Ext JS, LLC.
28545  *
28546  * Originally Released Under LGPL - original licence link has changed is not relivant.
28547  *
28548  * Fork - LGPL
28549  * <script type="text/javascript">
28550  */
28551 /**
28552  * @class Roo.TabPanel
28553  * @extends Roo.util.Observable
28554  * A lightweight tab container.
28555  * <br><br>
28556  * Usage:
28557  * <pre><code>
28558 // basic tabs 1, built from existing content
28559 var tabs = new Roo.TabPanel("tabs1");
28560 tabs.addTab("script", "View Script");
28561 tabs.addTab("markup", "View Markup");
28562 tabs.activate("script");
28563
28564 // more advanced tabs, built from javascript
28565 var jtabs = new Roo.TabPanel("jtabs");
28566 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28567
28568 // set up the UpdateManager
28569 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28570 var updater = tab2.getUpdateManager();
28571 updater.setDefaultUrl("ajax1.htm");
28572 tab2.on('activate', updater.refresh, updater, true);
28573
28574 // Use setUrl for Ajax loading
28575 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28576 tab3.setUrl("ajax2.htm", null, true);
28577
28578 // Disabled tab
28579 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28580 tab4.disable();
28581
28582 jtabs.activate("jtabs-1");
28583  * </code></pre>
28584  * @constructor
28585  * Create a new TabPanel.
28586  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28587  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28588  */
28589 Roo.TabPanel = function(container, config){
28590     /**
28591     * The container element for this TabPanel.
28592     * @type Roo.Element
28593     */
28594     this.el = Roo.get(container, true);
28595     if(config){
28596         if(typeof config == "boolean"){
28597             this.tabPosition = config ? "bottom" : "top";
28598         }else{
28599             Roo.apply(this, config);
28600         }
28601     }
28602     if(this.tabPosition == "bottom"){
28603         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28604         this.el.addClass("x-tabs-bottom");
28605     }
28606     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28607     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28608     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28609     if(Roo.isIE){
28610         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28611     }
28612     if(this.tabPosition != "bottom"){
28613         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28614          * @type Roo.Element
28615          */
28616         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28617         this.el.addClass("x-tabs-top");
28618     }
28619     this.items = [];
28620
28621     this.bodyEl.setStyle("position", "relative");
28622
28623     this.active = null;
28624     this.activateDelegate = this.activate.createDelegate(this);
28625
28626     this.addEvents({
28627         /**
28628          * @event tabchange
28629          * Fires when the active tab changes
28630          * @param {Roo.TabPanel} this
28631          * @param {Roo.TabPanelItem} activePanel The new active tab
28632          */
28633         "tabchange": true,
28634         /**
28635          * @event beforetabchange
28636          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28637          * @param {Roo.TabPanel} this
28638          * @param {Object} e Set cancel to true on this object to cancel the tab change
28639          * @param {Roo.TabPanelItem} tab The tab being changed to
28640          */
28641         "beforetabchange" : true
28642     });
28643
28644     Roo.EventManager.onWindowResize(this.onResize, this);
28645     this.cpad = this.el.getPadding("lr");
28646     this.hiddenCount = 0;
28647
28648
28649     // toolbar on the tabbar support...
28650     if (this.toolbar) {
28651         var tcfg = this.toolbar;
28652         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28653         this.toolbar = new Roo.Toolbar(tcfg);
28654         if (Roo.isSafari) {
28655             var tbl = tcfg.container.child('table', true);
28656             tbl.setAttribute('width', '100%');
28657         }
28658         
28659     }
28660    
28661
28662
28663     Roo.TabPanel.superclass.constructor.call(this);
28664 };
28665
28666 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28667     /*
28668      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28669      */
28670     tabPosition : "top",
28671     /*
28672      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28673      */
28674     currentTabWidth : 0,
28675     /*
28676      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28677      */
28678     minTabWidth : 40,
28679     /*
28680      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28681      */
28682     maxTabWidth : 250,
28683     /*
28684      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28685      */
28686     preferredTabWidth : 175,
28687     /*
28688      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28689      */
28690     resizeTabs : false,
28691     /*
28692      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28693      */
28694     monitorResize : true,
28695     /*
28696      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28697      */
28698     toolbar : false,
28699
28700     /**
28701      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28702      * @param {String} id The id of the div to use <b>or create</b>
28703      * @param {String} text The text for the tab
28704      * @param {String} content (optional) Content to put in the TabPanelItem body
28705      * @param {Boolean} closable (optional) True to create a close icon on the tab
28706      * @return {Roo.TabPanelItem} The created TabPanelItem
28707      */
28708     addTab : function(id, text, content, closable){
28709         var item = new Roo.TabPanelItem(this, id, text, closable);
28710         this.addTabItem(item);
28711         if(content){
28712             item.setContent(content);
28713         }
28714         return item;
28715     },
28716
28717     /**
28718      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28719      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28720      * @return {Roo.TabPanelItem}
28721      */
28722     getTab : function(id){
28723         return this.items[id];
28724     },
28725
28726     /**
28727      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28728      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28729      */
28730     hideTab : function(id){
28731         var t = this.items[id];
28732         if(!t.isHidden()){
28733            t.setHidden(true);
28734            this.hiddenCount++;
28735            this.autoSizeTabs();
28736         }
28737     },
28738
28739     /**
28740      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28741      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28742      */
28743     unhideTab : function(id){
28744         var t = this.items[id];
28745         if(t.isHidden()){
28746            t.setHidden(false);
28747            this.hiddenCount--;
28748            this.autoSizeTabs();
28749         }
28750     },
28751
28752     /**
28753      * Adds an existing {@link Roo.TabPanelItem}.
28754      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28755      */
28756     addTabItem : function(item){
28757         this.items[item.id] = item;
28758         this.items.push(item);
28759         if(this.resizeTabs){
28760            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28761            this.autoSizeTabs();
28762         }else{
28763             item.autoSize();
28764         }
28765     },
28766
28767     /**
28768      * Removes a {@link Roo.TabPanelItem}.
28769      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28770      */
28771     removeTab : function(id){
28772         var items = this.items;
28773         var tab = items[id];
28774         if(!tab) { return; }
28775         var index = items.indexOf(tab);
28776         if(this.active == tab && items.length > 1){
28777             var newTab = this.getNextAvailable(index);
28778             if(newTab) {
28779                 newTab.activate();
28780             }
28781         }
28782         this.stripEl.dom.removeChild(tab.pnode.dom);
28783         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28784             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28785         }
28786         items.splice(index, 1);
28787         delete this.items[tab.id];
28788         tab.fireEvent("close", tab);
28789         tab.purgeListeners();
28790         this.autoSizeTabs();
28791     },
28792
28793     getNextAvailable : function(start){
28794         var items = this.items;
28795         var index = start;
28796         // look for a next tab that will slide over to
28797         // replace the one being removed
28798         while(index < items.length){
28799             var item = items[++index];
28800             if(item && !item.isHidden()){
28801                 return item;
28802             }
28803         }
28804         // if one isn't found select the previous tab (on the left)
28805         index = start;
28806         while(index >= 0){
28807             var item = items[--index];
28808             if(item && !item.isHidden()){
28809                 return item;
28810             }
28811         }
28812         return null;
28813     },
28814
28815     /**
28816      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28817      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28818      */
28819     disableTab : function(id){
28820         var tab = this.items[id];
28821         if(tab && this.active != tab){
28822             tab.disable();
28823         }
28824     },
28825
28826     /**
28827      * Enables a {@link Roo.TabPanelItem} that is disabled.
28828      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28829      */
28830     enableTab : function(id){
28831         var tab = this.items[id];
28832         tab.enable();
28833     },
28834
28835     /**
28836      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28837      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28838      * @return {Roo.TabPanelItem} The TabPanelItem.
28839      */
28840     activate : function(id){
28841         var tab = this.items[id];
28842         if(!tab){
28843             return null;
28844         }
28845         if(tab == this.active || tab.disabled){
28846             return tab;
28847         }
28848         var e = {};
28849         this.fireEvent("beforetabchange", this, e, tab);
28850         if(e.cancel !== true && !tab.disabled){
28851             if(this.active){
28852                 this.active.hide();
28853             }
28854             this.active = this.items[id];
28855             this.active.show();
28856             this.fireEvent("tabchange", this, this.active);
28857         }
28858         return tab;
28859     },
28860
28861     /**
28862      * Gets the active {@link Roo.TabPanelItem}.
28863      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28864      */
28865     getActiveTab : function(){
28866         return this.active;
28867     },
28868
28869     /**
28870      * Updates the tab body element to fit the height of the container element
28871      * for overflow scrolling
28872      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28873      */
28874     syncHeight : function(targetHeight){
28875         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28876         var bm = this.bodyEl.getMargins();
28877         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28878         this.bodyEl.setHeight(newHeight);
28879         return newHeight;
28880     },
28881
28882     onResize : function(){
28883         if(this.monitorResize){
28884             this.autoSizeTabs();
28885         }
28886     },
28887
28888     /**
28889      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28890      */
28891     beginUpdate : function(){
28892         this.updating = true;
28893     },
28894
28895     /**
28896      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28897      */
28898     endUpdate : function(){
28899         this.updating = false;
28900         this.autoSizeTabs();
28901     },
28902
28903     /**
28904      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28905      */
28906     autoSizeTabs : function(){
28907         var count = this.items.length;
28908         var vcount = count - this.hiddenCount;
28909         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28910             return;
28911         }
28912         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28913         var availWidth = Math.floor(w / vcount);
28914         var b = this.stripBody;
28915         if(b.getWidth() > w){
28916             var tabs = this.items;
28917             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28918             if(availWidth < this.minTabWidth){
28919                 /*if(!this.sleft){    // incomplete scrolling code
28920                     this.createScrollButtons();
28921                 }
28922                 this.showScroll();
28923                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28924             }
28925         }else{
28926             if(this.currentTabWidth < this.preferredTabWidth){
28927                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28928             }
28929         }
28930     },
28931
28932     /**
28933      * Returns the number of tabs in this TabPanel.
28934      * @return {Number}
28935      */
28936      getCount : function(){
28937          return this.items.length;
28938      },
28939
28940     /**
28941      * Resizes all the tabs to the passed width
28942      * @param {Number} The new width
28943      */
28944     setTabWidth : function(width){
28945         this.currentTabWidth = width;
28946         for(var i = 0, len = this.items.length; i < len; i++) {
28947                 if(!this.items[i].isHidden()) {
28948                 this.items[i].setWidth(width);
28949             }
28950         }
28951     },
28952
28953     /**
28954      * Destroys this TabPanel
28955      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
28956      */
28957     destroy : function(removeEl){
28958         Roo.EventManager.removeResizeListener(this.onResize, this);
28959         for(var i = 0, len = this.items.length; i < len; i++){
28960             this.items[i].purgeListeners();
28961         }
28962         if(removeEl === true){
28963             this.el.update("");
28964             this.el.remove();
28965         }
28966     }
28967 });
28968
28969 /**
28970  * @class Roo.TabPanelItem
28971  * @extends Roo.util.Observable
28972  * Represents an individual item (tab plus body) in a TabPanel.
28973  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
28974  * @param {String} id The id of this TabPanelItem
28975  * @param {String} text The text for the tab of this TabPanelItem
28976  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
28977  */
28978 Roo.TabPanelItem = function(tabPanel, id, text, closable){
28979     /**
28980      * The {@link Roo.TabPanel} this TabPanelItem belongs to
28981      * @type Roo.TabPanel
28982      */
28983     this.tabPanel = tabPanel;
28984     /**
28985      * The id for this TabPanelItem
28986      * @type String
28987      */
28988     this.id = id;
28989     /** @private */
28990     this.disabled = false;
28991     /** @private */
28992     this.text = text;
28993     /** @private */
28994     this.loaded = false;
28995     this.closable = closable;
28996
28997     /**
28998      * The body element for this TabPanelItem.
28999      * @type Roo.Element
29000      */
29001     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29002     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29003     this.bodyEl.setStyle("display", "block");
29004     this.bodyEl.setStyle("zoom", "1");
29005     this.hideAction();
29006
29007     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29008     /** @private */
29009     this.el = Roo.get(els.el, true);
29010     this.inner = Roo.get(els.inner, true);
29011     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29012     this.pnode = Roo.get(els.el.parentNode, true);
29013     this.el.on("mousedown", this.onTabMouseDown, this);
29014     this.el.on("click", this.onTabClick, this);
29015     /** @private */
29016     if(closable){
29017         var c = Roo.get(els.close, true);
29018         c.dom.title = this.closeText;
29019         c.addClassOnOver("close-over");
29020         c.on("click", this.closeClick, this);
29021      }
29022
29023     this.addEvents({
29024          /**
29025          * @event activate
29026          * Fires when this tab becomes the active tab.
29027          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29028          * @param {Roo.TabPanelItem} this
29029          */
29030         "activate": true,
29031         /**
29032          * @event beforeclose
29033          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29034          * @param {Roo.TabPanelItem} this
29035          * @param {Object} e Set cancel to true on this object to cancel the close.
29036          */
29037         "beforeclose": true,
29038         /**
29039          * @event close
29040          * Fires when this tab is closed.
29041          * @param {Roo.TabPanelItem} this
29042          */
29043          "close": true,
29044         /**
29045          * @event deactivate
29046          * Fires when this tab is no longer the active tab.
29047          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29048          * @param {Roo.TabPanelItem} this
29049          */
29050          "deactivate" : true
29051     });
29052     this.hidden = false;
29053
29054     Roo.TabPanelItem.superclass.constructor.call(this);
29055 };
29056
29057 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29058     purgeListeners : function(){
29059        Roo.util.Observable.prototype.purgeListeners.call(this);
29060        this.el.removeAllListeners();
29061     },
29062     /**
29063      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29064      */
29065     show : function(){
29066         this.pnode.addClass("on");
29067         this.showAction();
29068         if(Roo.isOpera){
29069             this.tabPanel.stripWrap.repaint();
29070         }
29071         this.fireEvent("activate", this.tabPanel, this);
29072     },
29073
29074     /**
29075      * Returns true if this tab is the active tab.
29076      * @return {Boolean}
29077      */
29078     isActive : function(){
29079         return this.tabPanel.getActiveTab() == this;
29080     },
29081
29082     /**
29083      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29084      */
29085     hide : function(){
29086         this.pnode.removeClass("on");
29087         this.hideAction();
29088         this.fireEvent("deactivate", this.tabPanel, this);
29089     },
29090
29091     hideAction : function(){
29092         this.bodyEl.hide();
29093         this.bodyEl.setStyle("position", "absolute");
29094         this.bodyEl.setLeft("-20000px");
29095         this.bodyEl.setTop("-20000px");
29096     },
29097
29098     showAction : function(){
29099         this.bodyEl.setStyle("position", "relative");
29100         this.bodyEl.setTop("");
29101         this.bodyEl.setLeft("");
29102         this.bodyEl.show();
29103     },
29104
29105     /**
29106      * Set the tooltip for the tab.
29107      * @param {String} tooltip The tab's tooltip
29108      */
29109     setTooltip : function(text){
29110         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29111             this.textEl.dom.qtip = text;
29112             this.textEl.dom.removeAttribute('title');
29113         }else{
29114             this.textEl.dom.title = text;
29115         }
29116     },
29117
29118     onTabClick : function(e){
29119         e.preventDefault();
29120         this.tabPanel.activate(this.id);
29121     },
29122
29123     onTabMouseDown : function(e){
29124         e.preventDefault();
29125         this.tabPanel.activate(this.id);
29126     },
29127
29128     getWidth : function(){
29129         return this.inner.getWidth();
29130     },
29131
29132     setWidth : function(width){
29133         var iwidth = width - this.pnode.getPadding("lr");
29134         this.inner.setWidth(iwidth);
29135         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29136         this.pnode.setWidth(width);
29137     },
29138
29139     /**
29140      * Show or hide the tab
29141      * @param {Boolean} hidden True to hide or false to show.
29142      */
29143     setHidden : function(hidden){
29144         this.hidden = hidden;
29145         this.pnode.setStyle("display", hidden ? "none" : "");
29146     },
29147
29148     /**
29149      * Returns true if this tab is "hidden"
29150      * @return {Boolean}
29151      */
29152     isHidden : function(){
29153         return this.hidden;
29154     },
29155
29156     /**
29157      * Returns the text for this tab
29158      * @return {String}
29159      */
29160     getText : function(){
29161         return this.text;
29162     },
29163
29164     autoSize : function(){
29165         //this.el.beginMeasure();
29166         this.textEl.setWidth(1);
29167         /*
29168          *  #2804 [new] Tabs in Roojs
29169          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29170          */
29171         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29172         //this.el.endMeasure();
29173     },
29174
29175     /**
29176      * Sets the text for the tab (Note: this also sets the tooltip text)
29177      * @param {String} text The tab's text and tooltip
29178      */
29179     setText : function(text){
29180         this.text = text;
29181         this.textEl.update(text);
29182         this.setTooltip(text);
29183         if(!this.tabPanel.resizeTabs){
29184             this.autoSize();
29185         }
29186     },
29187     /**
29188      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29189      */
29190     activate : function(){
29191         this.tabPanel.activate(this.id);
29192     },
29193
29194     /**
29195      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29196      */
29197     disable : function(){
29198         if(this.tabPanel.active != this){
29199             this.disabled = true;
29200             this.pnode.addClass("disabled");
29201         }
29202     },
29203
29204     /**
29205      * Enables this TabPanelItem if it was previously disabled.
29206      */
29207     enable : function(){
29208         this.disabled = false;
29209         this.pnode.removeClass("disabled");
29210     },
29211
29212     /**
29213      * Sets the content for this TabPanelItem.
29214      * @param {String} content The content
29215      * @param {Boolean} loadScripts true to look for and load scripts
29216      */
29217     setContent : function(content, loadScripts){
29218         this.bodyEl.update(content, loadScripts);
29219     },
29220
29221     /**
29222      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29223      * @return {Roo.UpdateManager} The UpdateManager
29224      */
29225     getUpdateManager : function(){
29226         return this.bodyEl.getUpdateManager();
29227     },
29228
29229     /**
29230      * Set a URL to be used to load the content for this TabPanelItem.
29231      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29232      * @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)
29233      * @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)
29234      * @return {Roo.UpdateManager} The UpdateManager
29235      */
29236     setUrl : function(url, params, loadOnce){
29237         if(this.refreshDelegate){
29238             this.un('activate', this.refreshDelegate);
29239         }
29240         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29241         this.on("activate", this.refreshDelegate);
29242         return this.bodyEl.getUpdateManager();
29243     },
29244
29245     /** @private */
29246     _handleRefresh : function(url, params, loadOnce){
29247         if(!loadOnce || !this.loaded){
29248             var updater = this.bodyEl.getUpdateManager();
29249             updater.update(url, params, this._setLoaded.createDelegate(this));
29250         }
29251     },
29252
29253     /**
29254      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29255      *   Will fail silently if the setUrl method has not been called.
29256      *   This does not activate the panel, just updates its content.
29257      */
29258     refresh : function(){
29259         if(this.refreshDelegate){
29260            this.loaded = false;
29261            this.refreshDelegate();
29262         }
29263     },
29264
29265     /** @private */
29266     _setLoaded : function(){
29267         this.loaded = true;
29268     },
29269
29270     /** @private */
29271     closeClick : function(e){
29272         var o = {};
29273         e.stopEvent();
29274         this.fireEvent("beforeclose", this, o);
29275         if(o.cancel !== true){
29276             this.tabPanel.removeTab(this.id);
29277         }
29278     },
29279     /**
29280      * The text displayed in the tooltip for the close icon.
29281      * @type String
29282      */
29283     closeText : "Close this tab"
29284 });
29285
29286 /** @private */
29287 Roo.TabPanel.prototype.createStrip = function(container){
29288     var strip = document.createElement("div");
29289     strip.className = "x-tabs-wrap";
29290     container.appendChild(strip);
29291     return strip;
29292 };
29293 /** @private */
29294 Roo.TabPanel.prototype.createStripList = function(strip){
29295     // div wrapper for retard IE
29296     // returns the "tr" element.
29297     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29298         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29299         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29300     return strip.firstChild.firstChild.firstChild.firstChild;
29301 };
29302 /** @private */
29303 Roo.TabPanel.prototype.createBody = function(container){
29304     var body = document.createElement("div");
29305     Roo.id(body, "tab-body");
29306     Roo.fly(body).addClass("x-tabs-body");
29307     container.appendChild(body);
29308     return body;
29309 };
29310 /** @private */
29311 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29312     var body = Roo.getDom(id);
29313     if(!body){
29314         body = document.createElement("div");
29315         body.id = id;
29316     }
29317     Roo.fly(body).addClass("x-tabs-item-body");
29318     bodyEl.insertBefore(body, bodyEl.firstChild);
29319     return body;
29320 };
29321 /** @private */
29322 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29323     var td = document.createElement("td");
29324     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29325     //stripEl.appendChild(td);
29326     if(closable){
29327         td.className = "x-tabs-closable";
29328         if(!this.closeTpl){
29329             this.closeTpl = new Roo.Template(
29330                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29331                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29332                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29333             );
29334         }
29335         var el = this.closeTpl.overwrite(td, {"text": text});
29336         var close = el.getElementsByTagName("div")[0];
29337         var inner = el.getElementsByTagName("em")[0];
29338         return {"el": el, "close": close, "inner": inner};
29339     } else {
29340         if(!this.tabTpl){
29341             this.tabTpl = new Roo.Template(
29342                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29343                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29344             );
29345         }
29346         var el = this.tabTpl.overwrite(td, {"text": text});
29347         var inner = el.getElementsByTagName("em")[0];
29348         return {"el": el, "inner": inner};
29349     }
29350 };/*
29351  * Based on:
29352  * Ext JS Library 1.1.1
29353  * Copyright(c) 2006-2007, Ext JS, LLC.
29354  *
29355  * Originally Released Under LGPL - original licence link has changed is not relivant.
29356  *
29357  * Fork - LGPL
29358  * <script type="text/javascript">
29359  */
29360
29361 /**
29362  * @class Roo.Button
29363  * @extends Roo.util.Observable
29364  * Simple Button class
29365  * @cfg {String} text The button text
29366  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29367  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29368  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29369  * @cfg {Object} scope The scope of the handler
29370  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29371  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29372  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29373  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29374  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29375  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29376    applies if enableToggle = true)
29377  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29378  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29379   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29380  * @constructor
29381  * Create a new button
29382  * @param {Object} config The config object
29383  */
29384 Roo.Button = function(renderTo, config)
29385 {
29386     if (!config) {
29387         config = renderTo;
29388         renderTo = config.renderTo || false;
29389     }
29390     
29391     Roo.apply(this, config);
29392     this.addEvents({
29393         /**
29394              * @event click
29395              * Fires when this button is clicked
29396              * @param {Button} this
29397              * @param {EventObject} e The click event
29398              */
29399             "click" : true,
29400         /**
29401              * @event toggle
29402              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29403              * @param {Button} this
29404              * @param {Boolean} pressed
29405              */
29406             "toggle" : true,
29407         /**
29408              * @event mouseover
29409              * Fires when the mouse hovers over the button
29410              * @param {Button} this
29411              * @param {Event} e The event object
29412              */
29413         'mouseover' : true,
29414         /**
29415              * @event mouseout
29416              * Fires when the mouse exits the button
29417              * @param {Button} this
29418              * @param {Event} e The event object
29419              */
29420         'mouseout': true,
29421          /**
29422              * @event render
29423              * Fires when the button is rendered
29424              * @param {Button} this
29425              */
29426         'render': true
29427     });
29428     if(this.menu){
29429         this.menu = Roo.menu.MenuMgr.get(this.menu);
29430     }
29431     // register listeners first!!  - so render can be captured..
29432     Roo.util.Observable.call(this);
29433     if(renderTo){
29434         this.render(renderTo);
29435     }
29436     
29437   
29438 };
29439
29440 Roo.extend(Roo.Button, Roo.util.Observable, {
29441     /**
29442      * 
29443      */
29444     
29445     /**
29446      * Read-only. True if this button is hidden
29447      * @type Boolean
29448      */
29449     hidden : false,
29450     /**
29451      * Read-only. True if this button is disabled
29452      * @type Boolean
29453      */
29454     disabled : false,
29455     /**
29456      * Read-only. True if this button is pressed (only if enableToggle = true)
29457      * @type Boolean
29458      */
29459     pressed : false,
29460
29461     /**
29462      * @cfg {Number} tabIndex 
29463      * The DOM tabIndex for this button (defaults to undefined)
29464      */
29465     tabIndex : undefined,
29466
29467     /**
29468      * @cfg {Boolean} enableToggle
29469      * True to enable pressed/not pressed toggling (defaults to false)
29470      */
29471     enableToggle: false,
29472     /**
29473      * @cfg {Mixed} menu
29474      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29475      */
29476     menu : undefined,
29477     /**
29478      * @cfg {String} menuAlign
29479      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29480      */
29481     menuAlign : "tl-bl?",
29482
29483     /**
29484      * @cfg {String} iconCls
29485      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29486      */
29487     iconCls : undefined,
29488     /**
29489      * @cfg {String} type
29490      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29491      */
29492     type : 'button',
29493
29494     // private
29495     menuClassTarget: 'tr',
29496
29497     /**
29498      * @cfg {String} clickEvent
29499      * The type of event to map to the button's event handler (defaults to 'click')
29500      */
29501     clickEvent : 'click',
29502
29503     /**
29504      * @cfg {Boolean} handleMouseEvents
29505      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29506      */
29507     handleMouseEvents : true,
29508
29509     /**
29510      * @cfg {String} tooltipType
29511      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29512      */
29513     tooltipType : 'qtip',
29514
29515     /**
29516      * @cfg {String} cls
29517      * A CSS class to apply to the button's main element.
29518      */
29519     
29520     /**
29521      * @cfg {Roo.Template} template (Optional)
29522      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29523      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29524      * require code modifications if required elements (e.g. a button) aren't present.
29525      */
29526
29527     // private
29528     render : function(renderTo){
29529         var btn;
29530         if(this.hideParent){
29531             this.parentEl = Roo.get(renderTo);
29532         }
29533         if(!this.dhconfig){
29534             if(!this.template){
29535                 if(!Roo.Button.buttonTemplate){
29536                     // hideous table template
29537                     Roo.Button.buttonTemplate = new Roo.Template(
29538                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29539                         '<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>',
29540                         "</tr></tbody></table>");
29541                 }
29542                 this.template = Roo.Button.buttonTemplate;
29543             }
29544             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29545             var btnEl = btn.child("button:first");
29546             btnEl.on('focus', this.onFocus, this);
29547             btnEl.on('blur', this.onBlur, this);
29548             if(this.cls){
29549                 btn.addClass(this.cls);
29550             }
29551             if(this.icon){
29552                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29553             }
29554             if(this.iconCls){
29555                 btnEl.addClass(this.iconCls);
29556                 if(!this.cls){
29557                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29558                 }
29559             }
29560             if(this.tabIndex !== undefined){
29561                 btnEl.dom.tabIndex = this.tabIndex;
29562             }
29563             if(this.tooltip){
29564                 if(typeof this.tooltip == 'object'){
29565                     Roo.QuickTips.tips(Roo.apply({
29566                           target: btnEl.id
29567                     }, this.tooltip));
29568                 } else {
29569                     btnEl.dom[this.tooltipType] = this.tooltip;
29570                 }
29571             }
29572         }else{
29573             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29574         }
29575         this.el = btn;
29576         if(this.id){
29577             this.el.dom.id = this.el.id = this.id;
29578         }
29579         if(this.menu){
29580             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29581             this.menu.on("show", this.onMenuShow, this);
29582             this.menu.on("hide", this.onMenuHide, this);
29583         }
29584         btn.addClass("x-btn");
29585         if(Roo.isIE && !Roo.isIE7){
29586             this.autoWidth.defer(1, this);
29587         }else{
29588             this.autoWidth();
29589         }
29590         if(this.handleMouseEvents){
29591             btn.on("mouseover", this.onMouseOver, this);
29592             btn.on("mouseout", this.onMouseOut, this);
29593             btn.on("mousedown", this.onMouseDown, this);
29594         }
29595         btn.on(this.clickEvent, this.onClick, this);
29596         //btn.on("mouseup", this.onMouseUp, this);
29597         if(this.hidden){
29598             this.hide();
29599         }
29600         if(this.disabled){
29601             this.disable();
29602         }
29603         Roo.ButtonToggleMgr.register(this);
29604         if(this.pressed){
29605             this.el.addClass("x-btn-pressed");
29606         }
29607         if(this.repeat){
29608             var repeater = new Roo.util.ClickRepeater(btn,
29609                 typeof this.repeat == "object" ? this.repeat : {}
29610             );
29611             repeater.on("click", this.onClick,  this);
29612         }
29613         
29614         this.fireEvent('render', this);
29615         
29616     },
29617     /**
29618      * Returns the button's underlying element
29619      * @return {Roo.Element} The element
29620      */
29621     getEl : function(){
29622         return this.el;  
29623     },
29624     
29625     /**
29626      * Destroys this Button and removes any listeners.
29627      */
29628     destroy : function(){
29629         Roo.ButtonToggleMgr.unregister(this);
29630         this.el.removeAllListeners();
29631         this.purgeListeners();
29632         this.el.remove();
29633     },
29634
29635     // private
29636     autoWidth : function(){
29637         if(this.el){
29638             this.el.setWidth("auto");
29639             if(Roo.isIE7 && Roo.isStrict){
29640                 var ib = this.el.child('button');
29641                 if(ib && ib.getWidth() > 20){
29642                     ib.clip();
29643                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29644                 }
29645             }
29646             if(this.minWidth){
29647                 if(this.hidden){
29648                     this.el.beginMeasure();
29649                 }
29650                 if(this.el.getWidth() < this.minWidth){
29651                     this.el.setWidth(this.minWidth);
29652                 }
29653                 if(this.hidden){
29654                     this.el.endMeasure();
29655                 }
29656             }
29657         }
29658     },
29659
29660     /**
29661      * Assigns this button's click handler
29662      * @param {Function} handler The function to call when the button is clicked
29663      * @param {Object} scope (optional) Scope for the function passed in
29664      */
29665     setHandler : function(handler, scope){
29666         this.handler = handler;
29667         this.scope = scope;  
29668     },
29669     
29670     /**
29671      * Sets this button's text
29672      * @param {String} text The button text
29673      */
29674     setText : function(text){
29675         this.text = text;
29676         if(this.el){
29677             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29678         }
29679         this.autoWidth();
29680     },
29681     
29682     /**
29683      * Gets the text for this button
29684      * @return {String} The button text
29685      */
29686     getText : function(){
29687         return this.text;  
29688     },
29689     
29690     /**
29691      * Show this button
29692      */
29693     show: function(){
29694         this.hidden = false;
29695         if(this.el){
29696             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29697         }
29698     },
29699     
29700     /**
29701      * Hide this button
29702      */
29703     hide: function(){
29704         this.hidden = true;
29705         if(this.el){
29706             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29707         }
29708     },
29709     
29710     /**
29711      * Convenience function for boolean show/hide
29712      * @param {Boolean} visible True to show, false to hide
29713      */
29714     setVisible: function(visible){
29715         if(visible) {
29716             this.show();
29717         }else{
29718             this.hide();
29719         }
29720     },
29721     
29722     /**
29723      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29724      * @param {Boolean} state (optional) Force a particular state
29725      */
29726     toggle : function(state){
29727         state = state === undefined ? !this.pressed : state;
29728         if(state != this.pressed){
29729             if(state){
29730                 this.el.addClass("x-btn-pressed");
29731                 this.pressed = true;
29732                 this.fireEvent("toggle", this, true);
29733             }else{
29734                 this.el.removeClass("x-btn-pressed");
29735                 this.pressed = false;
29736                 this.fireEvent("toggle", this, false);
29737             }
29738             if(this.toggleHandler){
29739                 this.toggleHandler.call(this.scope || this, this, state);
29740             }
29741         }
29742     },
29743     
29744     /**
29745      * Focus the button
29746      */
29747     focus : function(){
29748         this.el.child('button:first').focus();
29749     },
29750     
29751     /**
29752      * Disable this button
29753      */
29754     disable : function(){
29755         if(this.el){
29756             this.el.addClass("x-btn-disabled");
29757         }
29758         this.disabled = true;
29759     },
29760     
29761     /**
29762      * Enable this button
29763      */
29764     enable : function(){
29765         if(this.el){
29766             this.el.removeClass("x-btn-disabled");
29767         }
29768         this.disabled = false;
29769     },
29770
29771     /**
29772      * Convenience function for boolean enable/disable
29773      * @param {Boolean} enabled True to enable, false to disable
29774      */
29775     setDisabled : function(v){
29776         this[v !== true ? "enable" : "disable"]();
29777     },
29778
29779     // private
29780     onClick : function(e)
29781     {
29782         if(e){
29783             e.preventDefault();
29784         }
29785         if(e.button != 0){
29786             return;
29787         }
29788         if(!this.disabled){
29789             if(this.enableToggle){
29790                 this.toggle();
29791             }
29792             if(this.menu && !this.menu.isVisible()){
29793                 this.menu.show(this.el, this.menuAlign);
29794             }
29795             this.fireEvent("click", this, e);
29796             if(this.handler){
29797                 this.el.removeClass("x-btn-over");
29798                 this.handler.call(this.scope || this, this, e);
29799             }
29800         }
29801     },
29802     // private
29803     onMouseOver : function(e){
29804         if(!this.disabled){
29805             this.el.addClass("x-btn-over");
29806             this.fireEvent('mouseover', this, e);
29807         }
29808     },
29809     // private
29810     onMouseOut : function(e){
29811         if(!e.within(this.el,  true)){
29812             this.el.removeClass("x-btn-over");
29813             this.fireEvent('mouseout', this, e);
29814         }
29815     },
29816     // private
29817     onFocus : function(e){
29818         if(!this.disabled){
29819             this.el.addClass("x-btn-focus");
29820         }
29821     },
29822     // private
29823     onBlur : function(e){
29824         this.el.removeClass("x-btn-focus");
29825     },
29826     // private
29827     onMouseDown : function(e){
29828         if(!this.disabled && e.button == 0){
29829             this.el.addClass("x-btn-click");
29830             Roo.get(document).on('mouseup', this.onMouseUp, this);
29831         }
29832     },
29833     // private
29834     onMouseUp : function(e){
29835         if(e.button == 0){
29836             this.el.removeClass("x-btn-click");
29837             Roo.get(document).un('mouseup', this.onMouseUp, this);
29838         }
29839     },
29840     // private
29841     onMenuShow : function(e){
29842         this.el.addClass("x-btn-menu-active");
29843     },
29844     // private
29845     onMenuHide : function(e){
29846         this.el.removeClass("x-btn-menu-active");
29847     }   
29848 });
29849
29850 // Private utility class used by Button
29851 Roo.ButtonToggleMgr = function(){
29852    var groups = {};
29853    
29854    function toggleGroup(btn, state){
29855        if(state){
29856            var g = groups[btn.toggleGroup];
29857            for(var i = 0, l = g.length; i < l; i++){
29858                if(g[i] != btn){
29859                    g[i].toggle(false);
29860                }
29861            }
29862        }
29863    }
29864    
29865    return {
29866        register : function(btn){
29867            if(!btn.toggleGroup){
29868                return;
29869            }
29870            var g = groups[btn.toggleGroup];
29871            if(!g){
29872                g = groups[btn.toggleGroup] = [];
29873            }
29874            g.push(btn);
29875            btn.on("toggle", toggleGroup);
29876        },
29877        
29878        unregister : function(btn){
29879            if(!btn.toggleGroup){
29880                return;
29881            }
29882            var g = groups[btn.toggleGroup];
29883            if(g){
29884                g.remove(btn);
29885                btn.un("toggle", toggleGroup);
29886            }
29887        }
29888    };
29889 }();/*
29890  * Based on:
29891  * Ext JS Library 1.1.1
29892  * Copyright(c) 2006-2007, Ext JS, LLC.
29893  *
29894  * Originally Released Under LGPL - original licence link has changed is not relivant.
29895  *
29896  * Fork - LGPL
29897  * <script type="text/javascript">
29898  */
29899  
29900 /**
29901  * @class Roo.SplitButton
29902  * @extends Roo.Button
29903  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29904  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29905  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29906  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29907  * @cfg {String} arrowTooltip The title attribute of the arrow
29908  * @constructor
29909  * Create a new menu button
29910  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29911  * @param {Object} config The config object
29912  */
29913 Roo.SplitButton = function(renderTo, config){
29914     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29915     /**
29916      * @event arrowclick
29917      * Fires when this button's arrow is clicked
29918      * @param {SplitButton} this
29919      * @param {EventObject} e The click event
29920      */
29921     this.addEvents({"arrowclick":true});
29922 };
29923
29924 Roo.extend(Roo.SplitButton, Roo.Button, {
29925     render : function(renderTo){
29926         // this is one sweet looking template!
29927         var tpl = new Roo.Template(
29928             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29929             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29930             '<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>',
29931             "</tbody></table></td><td>",
29932             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29933             '<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>',
29934             "</tbody></table></td></tr></table>"
29935         );
29936         var btn = tpl.append(renderTo, [this.text, this.type], true);
29937         var btnEl = btn.child("button");
29938         if(this.cls){
29939             btn.addClass(this.cls);
29940         }
29941         if(this.icon){
29942             btnEl.setStyle('background-image', 'url(' +this.icon +')');
29943         }
29944         if(this.iconCls){
29945             btnEl.addClass(this.iconCls);
29946             if(!this.cls){
29947                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29948             }
29949         }
29950         this.el = btn;
29951         if(this.handleMouseEvents){
29952             btn.on("mouseover", this.onMouseOver, this);
29953             btn.on("mouseout", this.onMouseOut, this);
29954             btn.on("mousedown", this.onMouseDown, this);
29955             btn.on("mouseup", this.onMouseUp, this);
29956         }
29957         btn.on(this.clickEvent, this.onClick, this);
29958         if(this.tooltip){
29959             if(typeof this.tooltip == 'object'){
29960                 Roo.QuickTips.tips(Roo.apply({
29961                       target: btnEl.id
29962                 }, this.tooltip));
29963             } else {
29964                 btnEl.dom[this.tooltipType] = this.tooltip;
29965             }
29966         }
29967         if(this.arrowTooltip){
29968             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
29969         }
29970         if(this.hidden){
29971             this.hide();
29972         }
29973         if(this.disabled){
29974             this.disable();
29975         }
29976         if(this.pressed){
29977             this.el.addClass("x-btn-pressed");
29978         }
29979         if(Roo.isIE && !Roo.isIE7){
29980             this.autoWidth.defer(1, this);
29981         }else{
29982             this.autoWidth();
29983         }
29984         if(this.menu){
29985             this.menu.on("show", this.onMenuShow, this);
29986             this.menu.on("hide", this.onMenuHide, this);
29987         }
29988         this.fireEvent('render', this);
29989     },
29990
29991     // private
29992     autoWidth : function(){
29993         if(this.el){
29994             var tbl = this.el.child("table:first");
29995             var tbl2 = this.el.child("table:last");
29996             this.el.setWidth("auto");
29997             tbl.setWidth("auto");
29998             if(Roo.isIE7 && Roo.isStrict){
29999                 var ib = this.el.child('button:first');
30000                 if(ib && ib.getWidth() > 20){
30001                     ib.clip();
30002                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30003                 }
30004             }
30005             if(this.minWidth){
30006                 if(this.hidden){
30007                     this.el.beginMeasure();
30008                 }
30009                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30010                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30011                 }
30012                 if(this.hidden){
30013                     this.el.endMeasure();
30014                 }
30015             }
30016             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30017         } 
30018     },
30019     /**
30020      * Sets this button's click handler
30021      * @param {Function} handler The function to call when the button is clicked
30022      * @param {Object} scope (optional) Scope for the function passed above
30023      */
30024     setHandler : function(handler, scope){
30025         this.handler = handler;
30026         this.scope = scope;  
30027     },
30028     
30029     /**
30030      * Sets this button's arrow click handler
30031      * @param {Function} handler The function to call when the arrow is clicked
30032      * @param {Object} scope (optional) Scope for the function passed above
30033      */
30034     setArrowHandler : function(handler, scope){
30035         this.arrowHandler = handler;
30036         this.scope = scope;  
30037     },
30038     
30039     /**
30040      * Focus the button
30041      */
30042     focus : function(){
30043         if(this.el){
30044             this.el.child("button:first").focus();
30045         }
30046     },
30047
30048     // private
30049     onClick : function(e){
30050         e.preventDefault();
30051         if(!this.disabled){
30052             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30053                 if(this.menu && !this.menu.isVisible()){
30054                     this.menu.show(this.el, this.menuAlign);
30055                 }
30056                 this.fireEvent("arrowclick", this, e);
30057                 if(this.arrowHandler){
30058                     this.arrowHandler.call(this.scope || this, this, e);
30059                 }
30060             }else{
30061                 this.fireEvent("click", this, e);
30062                 if(this.handler){
30063                     this.handler.call(this.scope || this, this, e);
30064                 }
30065             }
30066         }
30067     },
30068     // private
30069     onMouseDown : function(e){
30070         if(!this.disabled){
30071             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30072         }
30073     },
30074     // private
30075     onMouseUp : function(e){
30076         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30077     }   
30078 });
30079
30080
30081 // backwards compat
30082 Roo.MenuButton = Roo.SplitButton;/*
30083  * Based on:
30084  * Ext JS Library 1.1.1
30085  * Copyright(c) 2006-2007, Ext JS, LLC.
30086  *
30087  * Originally Released Under LGPL - original licence link has changed is not relivant.
30088  *
30089  * Fork - LGPL
30090  * <script type="text/javascript">
30091  */
30092
30093 /**
30094  * @class Roo.Toolbar
30095  * Basic Toolbar class.
30096  * @constructor
30097  * Creates a new Toolbar
30098  * @param {Object} container The config object
30099  */ 
30100 Roo.Toolbar = function(container, buttons, config)
30101 {
30102     /// old consturctor format still supported..
30103     if(container instanceof Array){ // omit the container for later rendering
30104         buttons = container;
30105         config = buttons;
30106         container = null;
30107     }
30108     if (typeof(container) == 'object' && container.xtype) {
30109         config = container;
30110         container = config.container;
30111         buttons = config.buttons || []; // not really - use items!!
30112     }
30113     var xitems = [];
30114     if (config && config.items) {
30115         xitems = config.items;
30116         delete config.items;
30117     }
30118     Roo.apply(this, config);
30119     this.buttons = buttons;
30120     
30121     if(container){
30122         this.render(container);
30123     }
30124     this.xitems = xitems;
30125     Roo.each(xitems, function(b) {
30126         this.add(b);
30127     }, this);
30128     
30129 };
30130
30131 Roo.Toolbar.prototype = {
30132     /**
30133      * @cfg {Array} items
30134      * array of button configs or elements to add (will be converted to a MixedCollection)
30135      */
30136     
30137     /**
30138      * @cfg {String/HTMLElement/Element} container
30139      * The id or element that will contain the toolbar
30140      */
30141     // private
30142     render : function(ct){
30143         this.el = Roo.get(ct);
30144         if(this.cls){
30145             this.el.addClass(this.cls);
30146         }
30147         // using a table allows for vertical alignment
30148         // 100% width is needed by Safari...
30149         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30150         this.tr = this.el.child("tr", true);
30151         var autoId = 0;
30152         this.items = new Roo.util.MixedCollection(false, function(o){
30153             return o.id || ("item" + (++autoId));
30154         });
30155         if(this.buttons){
30156             this.add.apply(this, this.buttons);
30157             delete this.buttons;
30158         }
30159     },
30160
30161     /**
30162      * Adds element(s) to the toolbar -- this function takes a variable number of 
30163      * arguments of mixed type and adds them to the toolbar.
30164      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30165      * <ul>
30166      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30167      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30168      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30169      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30170      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30171      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30172      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30173      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30174      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30175      * </ul>
30176      * @param {Mixed} arg2
30177      * @param {Mixed} etc.
30178      */
30179     add : function(){
30180         var a = arguments, l = a.length;
30181         for(var i = 0; i < l; i++){
30182             this._add(a[i]);
30183         }
30184     },
30185     // private..
30186     _add : function(el) {
30187         
30188         if (el.xtype) {
30189             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30190         }
30191         
30192         if (el.applyTo){ // some kind of form field
30193             return this.addField(el);
30194         } 
30195         if (el.render){ // some kind of Toolbar.Item
30196             return this.addItem(el);
30197         }
30198         if (typeof el == "string"){ // string
30199             if(el == "separator" || el == "-"){
30200                 return this.addSeparator();
30201             }
30202             if (el == " "){
30203                 return this.addSpacer();
30204             }
30205             if(el == "->"){
30206                 return this.addFill();
30207             }
30208             return this.addText(el);
30209             
30210         }
30211         if(el.tagName){ // element
30212             return this.addElement(el);
30213         }
30214         if(typeof el == "object"){ // must be button config?
30215             return this.addButton(el);
30216         }
30217         // and now what?!?!
30218         return false;
30219         
30220     },
30221     
30222     /**
30223      * Add an Xtype element
30224      * @param {Object} xtype Xtype Object
30225      * @return {Object} created Object
30226      */
30227     addxtype : function(e){
30228         return this.add(e);  
30229     },
30230     
30231     /**
30232      * Returns the Element for this toolbar.
30233      * @return {Roo.Element}
30234      */
30235     getEl : function(){
30236         return this.el;  
30237     },
30238     
30239     /**
30240      * Adds a separator
30241      * @return {Roo.Toolbar.Item} The separator item
30242      */
30243     addSeparator : function(){
30244         return this.addItem(new Roo.Toolbar.Separator());
30245     },
30246
30247     /**
30248      * Adds a spacer element
30249      * @return {Roo.Toolbar.Spacer} The spacer item
30250      */
30251     addSpacer : function(){
30252         return this.addItem(new Roo.Toolbar.Spacer());
30253     },
30254
30255     /**
30256      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30257      * @return {Roo.Toolbar.Fill} The fill item
30258      */
30259     addFill : function(){
30260         return this.addItem(new Roo.Toolbar.Fill());
30261     },
30262
30263     /**
30264      * Adds any standard HTML element to the toolbar
30265      * @param {String/HTMLElement/Element} el The element or id of the element to add
30266      * @return {Roo.Toolbar.Item} The element's item
30267      */
30268     addElement : function(el){
30269         return this.addItem(new Roo.Toolbar.Item(el));
30270     },
30271     /**
30272      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30273      * @type Roo.util.MixedCollection  
30274      */
30275     items : false,
30276      
30277     /**
30278      * Adds any Toolbar.Item or subclass
30279      * @param {Roo.Toolbar.Item} item
30280      * @return {Roo.Toolbar.Item} The item
30281      */
30282     addItem : function(item){
30283         var td = this.nextBlock();
30284         item.render(td);
30285         this.items.add(item);
30286         return item;
30287     },
30288     
30289     /**
30290      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30291      * @param {Object/Array} config A button config or array of configs
30292      * @return {Roo.Toolbar.Button/Array}
30293      */
30294     addButton : function(config){
30295         if(config instanceof Array){
30296             var buttons = [];
30297             for(var i = 0, len = config.length; i < len; i++) {
30298                 buttons.push(this.addButton(config[i]));
30299             }
30300             return buttons;
30301         }
30302         var b = config;
30303         if(!(config instanceof Roo.Toolbar.Button)){
30304             b = config.split ?
30305                 new Roo.Toolbar.SplitButton(config) :
30306                 new Roo.Toolbar.Button(config);
30307         }
30308         var td = this.nextBlock();
30309         b.render(td);
30310         this.items.add(b);
30311         return b;
30312     },
30313     
30314     /**
30315      * Adds text to the toolbar
30316      * @param {String} text The text to add
30317      * @return {Roo.Toolbar.Item} The element's item
30318      */
30319     addText : function(text){
30320         return this.addItem(new Roo.Toolbar.TextItem(text));
30321     },
30322     
30323     /**
30324      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30325      * @param {Number} index The index where the item is to be inserted
30326      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30327      * @return {Roo.Toolbar.Button/Item}
30328      */
30329     insertButton : function(index, item){
30330         if(item instanceof Array){
30331             var buttons = [];
30332             for(var i = 0, len = item.length; i < len; i++) {
30333                buttons.push(this.insertButton(index + i, item[i]));
30334             }
30335             return buttons;
30336         }
30337         if (!(item instanceof Roo.Toolbar.Button)){
30338            item = new Roo.Toolbar.Button(item);
30339         }
30340         var td = document.createElement("td");
30341         this.tr.insertBefore(td, this.tr.childNodes[index]);
30342         item.render(td);
30343         this.items.insert(index, item);
30344         return item;
30345     },
30346     
30347     /**
30348      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30349      * @param {Object} config
30350      * @return {Roo.Toolbar.Item} The element's item
30351      */
30352     addDom : function(config, returnEl){
30353         var td = this.nextBlock();
30354         Roo.DomHelper.overwrite(td, config);
30355         var ti = new Roo.Toolbar.Item(td.firstChild);
30356         ti.render(td);
30357         this.items.add(ti);
30358         return ti;
30359     },
30360
30361     /**
30362      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30363      * @type Roo.util.MixedCollection  
30364      */
30365     fields : false,
30366     
30367     /**
30368      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30369      * Note: the field should not have been rendered yet. For a field that has already been
30370      * rendered, use {@link #addElement}.
30371      * @param {Roo.form.Field} field
30372      * @return {Roo.ToolbarItem}
30373      */
30374      
30375       
30376     addField : function(field) {
30377         if (!this.fields) {
30378             var autoId = 0;
30379             this.fields = new Roo.util.MixedCollection(false, function(o){
30380                 return o.id || ("item" + (++autoId));
30381             });
30382
30383         }
30384         
30385         var td = this.nextBlock();
30386         field.render(td);
30387         var ti = new Roo.Toolbar.Item(td.firstChild);
30388         ti.render(td);
30389         this.items.add(ti);
30390         this.fields.add(field);
30391         return ti;
30392     },
30393     /**
30394      * Hide the toolbar
30395      * @method hide
30396      */
30397      
30398       
30399     hide : function()
30400     {
30401         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30402         this.el.child('div').hide();
30403     },
30404     /**
30405      * Show the toolbar
30406      * @method show
30407      */
30408     show : function()
30409     {
30410         this.el.child('div').show();
30411     },
30412       
30413     // private
30414     nextBlock : function(){
30415         var td = document.createElement("td");
30416         this.tr.appendChild(td);
30417         return td;
30418     },
30419
30420     // private
30421     destroy : function(){
30422         if(this.items){ // rendered?
30423             Roo.destroy.apply(Roo, this.items.items);
30424         }
30425         if(this.fields){ // rendered?
30426             Roo.destroy.apply(Roo, this.fields.items);
30427         }
30428         Roo.Element.uncache(this.el, this.tr);
30429     }
30430 };
30431
30432 /**
30433  * @class Roo.Toolbar.Item
30434  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30435  * @constructor
30436  * Creates a new Item
30437  * @param {HTMLElement} el 
30438  */
30439 Roo.Toolbar.Item = function(el){
30440     var cfg = {};
30441     if (typeof (el.xtype) != 'undefined') {
30442         cfg = el;
30443         el = cfg.el;
30444     }
30445     
30446     this.el = Roo.getDom(el);
30447     this.id = Roo.id(this.el);
30448     this.hidden = false;
30449     
30450     this.addEvents({
30451          /**
30452              * @event render
30453              * Fires when the button is rendered
30454              * @param {Button} this
30455              */
30456         'render': true
30457     });
30458     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30459 };
30460 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30461 //Roo.Toolbar.Item.prototype = {
30462     
30463     /**
30464      * Get this item's HTML Element
30465      * @return {HTMLElement}
30466      */
30467     getEl : function(){
30468        return this.el;  
30469     },
30470
30471     // private
30472     render : function(td){
30473         
30474          this.td = td;
30475         td.appendChild(this.el);
30476         
30477         this.fireEvent('render', this);
30478     },
30479     
30480     /**
30481      * Removes and destroys this item.
30482      */
30483     destroy : function(){
30484         this.td.parentNode.removeChild(this.td);
30485     },
30486     
30487     /**
30488      * Shows this item.
30489      */
30490     show: function(){
30491         this.hidden = false;
30492         this.td.style.display = "";
30493     },
30494     
30495     /**
30496      * Hides this item.
30497      */
30498     hide: function(){
30499         this.hidden = true;
30500         this.td.style.display = "none";
30501     },
30502     
30503     /**
30504      * Convenience function for boolean show/hide.
30505      * @param {Boolean} visible true to show/false to hide
30506      */
30507     setVisible: function(visible){
30508         if(visible) {
30509             this.show();
30510         }else{
30511             this.hide();
30512         }
30513     },
30514     
30515     /**
30516      * Try to focus this item.
30517      */
30518     focus : function(){
30519         Roo.fly(this.el).focus();
30520     },
30521     
30522     /**
30523      * Disables this item.
30524      */
30525     disable : function(){
30526         Roo.fly(this.td).addClass("x-item-disabled");
30527         this.disabled = true;
30528         this.el.disabled = true;
30529     },
30530     
30531     /**
30532      * Enables this item.
30533      */
30534     enable : function(){
30535         Roo.fly(this.td).removeClass("x-item-disabled");
30536         this.disabled = false;
30537         this.el.disabled = false;
30538     }
30539 });
30540
30541
30542 /**
30543  * @class Roo.Toolbar.Separator
30544  * @extends Roo.Toolbar.Item
30545  * A simple toolbar separator class
30546  * @constructor
30547  * Creates a new Separator
30548  */
30549 Roo.Toolbar.Separator = function(cfg){
30550     
30551     var s = document.createElement("span");
30552     s.className = "ytb-sep";
30553     if (cfg) {
30554         cfg.el = s;
30555     }
30556     
30557     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30558 };
30559 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30560     enable:Roo.emptyFn,
30561     disable:Roo.emptyFn,
30562     focus:Roo.emptyFn
30563 });
30564
30565 /**
30566  * @class Roo.Toolbar.Spacer
30567  * @extends Roo.Toolbar.Item
30568  * A simple element that adds extra horizontal space to a toolbar.
30569  * @constructor
30570  * Creates a new Spacer
30571  */
30572 Roo.Toolbar.Spacer = function(cfg){
30573     var s = document.createElement("div");
30574     s.className = "ytb-spacer";
30575     if (cfg) {
30576         cfg.el = s;
30577     }
30578     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30579 };
30580 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30581     enable:Roo.emptyFn,
30582     disable:Roo.emptyFn,
30583     focus:Roo.emptyFn
30584 });
30585
30586 /**
30587  * @class Roo.Toolbar.Fill
30588  * @extends Roo.Toolbar.Spacer
30589  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30590  * @constructor
30591  * Creates a new Spacer
30592  */
30593 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30594     // private
30595     render : function(td){
30596         td.style.width = '100%';
30597         Roo.Toolbar.Fill.superclass.render.call(this, td);
30598     }
30599 });
30600
30601 /**
30602  * @class Roo.Toolbar.TextItem
30603  * @extends Roo.Toolbar.Item
30604  * A simple class that renders text directly into a toolbar.
30605  * @constructor
30606  * Creates a new TextItem
30607  * @cfg {string} text 
30608  */
30609 Roo.Toolbar.TextItem = function(cfg){
30610     var  text = cfg || "";
30611     if (typeof(cfg) == 'object') {
30612         text = cfg.text || "";
30613     }  else {
30614         cfg = null;
30615     }
30616     var s = document.createElement("span");
30617     s.className = "ytb-text";
30618     s.innerHTML = text;
30619     if (cfg) {
30620         cfg.el  = s;
30621     }
30622     
30623     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30624 };
30625 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30626     
30627      
30628     enable:Roo.emptyFn,
30629     disable:Roo.emptyFn,
30630     focus:Roo.emptyFn
30631 });
30632
30633 /**
30634  * @class Roo.Toolbar.Button
30635  * @extends Roo.Button
30636  * A button that renders into a toolbar.
30637  * @constructor
30638  * Creates a new Button
30639  * @param {Object} config A standard {@link Roo.Button} config object
30640  */
30641 Roo.Toolbar.Button = function(config){
30642     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30643 };
30644 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30645     render : function(td){
30646         this.td = td;
30647         Roo.Toolbar.Button.superclass.render.call(this, td);
30648     },
30649     
30650     /**
30651      * Removes and destroys this button
30652      */
30653     destroy : function(){
30654         Roo.Toolbar.Button.superclass.destroy.call(this);
30655         this.td.parentNode.removeChild(this.td);
30656     },
30657     
30658     /**
30659      * Shows this button
30660      */
30661     show: function(){
30662         this.hidden = false;
30663         this.td.style.display = "";
30664     },
30665     
30666     /**
30667      * Hides this button
30668      */
30669     hide: function(){
30670         this.hidden = true;
30671         this.td.style.display = "none";
30672     },
30673
30674     /**
30675      * Disables this item
30676      */
30677     disable : function(){
30678         Roo.fly(this.td).addClass("x-item-disabled");
30679         this.disabled = true;
30680     },
30681
30682     /**
30683      * Enables this item
30684      */
30685     enable : function(){
30686         Roo.fly(this.td).removeClass("x-item-disabled");
30687         this.disabled = false;
30688     }
30689 });
30690 // backwards compat
30691 Roo.ToolbarButton = Roo.Toolbar.Button;
30692
30693 /**
30694  * @class Roo.Toolbar.SplitButton
30695  * @extends Roo.SplitButton
30696  * A menu button that renders into a toolbar.
30697  * @constructor
30698  * Creates a new SplitButton
30699  * @param {Object} config A standard {@link Roo.SplitButton} config object
30700  */
30701 Roo.Toolbar.SplitButton = function(config){
30702     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30703 };
30704 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30705     render : function(td){
30706         this.td = td;
30707         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30708     },
30709     
30710     /**
30711      * Removes and destroys this button
30712      */
30713     destroy : function(){
30714         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30715         this.td.parentNode.removeChild(this.td);
30716     },
30717     
30718     /**
30719      * Shows this button
30720      */
30721     show: function(){
30722         this.hidden = false;
30723         this.td.style.display = "";
30724     },
30725     
30726     /**
30727      * Hides this button
30728      */
30729     hide: function(){
30730         this.hidden = true;
30731         this.td.style.display = "none";
30732     }
30733 });
30734
30735 // backwards compat
30736 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30737  * Based on:
30738  * Ext JS Library 1.1.1
30739  * Copyright(c) 2006-2007, Ext JS, LLC.
30740  *
30741  * Originally Released Under LGPL - original licence link has changed is not relivant.
30742  *
30743  * Fork - LGPL
30744  * <script type="text/javascript">
30745  */
30746  
30747 /**
30748  * @class Roo.PagingToolbar
30749  * @extends Roo.Toolbar
30750  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30751  * @constructor
30752  * Create a new PagingToolbar
30753  * @param {Object} config The config object
30754  */
30755 Roo.PagingToolbar = function(el, ds, config)
30756 {
30757     // old args format still supported... - xtype is prefered..
30758     if (typeof(el) == 'object' && el.xtype) {
30759         // created from xtype...
30760         config = el;
30761         ds = el.dataSource;
30762         el = config.container;
30763     }
30764     var items = [];
30765     if (config.items) {
30766         items = config.items;
30767         config.items = [];
30768     }
30769     
30770     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30771     this.ds = ds;
30772     this.cursor = 0;
30773     this.renderButtons(this.el);
30774     this.bind(ds);
30775     
30776     // supprot items array.
30777    
30778     Roo.each(items, function(e) {
30779         this.add(Roo.factory(e));
30780     },this);
30781     
30782 };
30783
30784 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30785     /**
30786      * @cfg {Roo.data.Store} dataSource
30787      * The underlying data store providing the paged data
30788      */
30789     /**
30790      * @cfg {String/HTMLElement/Element} container
30791      * container The id or element that will contain the toolbar
30792      */
30793     /**
30794      * @cfg {Boolean} displayInfo
30795      * True to display the displayMsg (defaults to false)
30796      */
30797     /**
30798      * @cfg {Number} pageSize
30799      * The number of records to display per page (defaults to 20)
30800      */
30801     pageSize: 20,
30802     /**
30803      * @cfg {String} displayMsg
30804      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30805      */
30806     displayMsg : 'Displaying {0} - {1} of {2}',
30807     /**
30808      * @cfg {String} emptyMsg
30809      * The message to display when no records are found (defaults to "No data to display")
30810      */
30811     emptyMsg : 'No data to display',
30812     /**
30813      * Customizable piece of the default paging text (defaults to "Page")
30814      * @type String
30815      */
30816     beforePageText : "Page",
30817     /**
30818      * Customizable piece of the default paging text (defaults to "of %0")
30819      * @type String
30820      */
30821     afterPageText : "of {0}",
30822     /**
30823      * Customizable piece of the default paging text (defaults to "First Page")
30824      * @type String
30825      */
30826     firstText : "First Page",
30827     /**
30828      * Customizable piece of the default paging text (defaults to "Previous Page")
30829      * @type String
30830      */
30831     prevText : "Previous Page",
30832     /**
30833      * Customizable piece of the default paging text (defaults to "Next Page")
30834      * @type String
30835      */
30836     nextText : "Next Page",
30837     /**
30838      * Customizable piece of the default paging text (defaults to "Last Page")
30839      * @type String
30840      */
30841     lastText : "Last Page",
30842     /**
30843      * Customizable piece of the default paging text (defaults to "Refresh")
30844      * @type String
30845      */
30846     refreshText : "Refresh",
30847
30848     // private
30849     renderButtons : function(el){
30850         Roo.PagingToolbar.superclass.render.call(this, el);
30851         this.first = this.addButton({
30852             tooltip: this.firstText,
30853             cls: "x-btn-icon x-grid-page-first",
30854             disabled: true,
30855             handler: this.onClick.createDelegate(this, ["first"])
30856         });
30857         this.prev = this.addButton({
30858             tooltip: this.prevText,
30859             cls: "x-btn-icon x-grid-page-prev",
30860             disabled: true,
30861             handler: this.onClick.createDelegate(this, ["prev"])
30862         });
30863         //this.addSeparator();
30864         this.add(this.beforePageText);
30865         this.field = Roo.get(this.addDom({
30866            tag: "input",
30867            type: "text",
30868            size: "3",
30869            value: "1",
30870            cls: "x-grid-page-number"
30871         }).el);
30872         this.field.on("keydown", this.onPagingKeydown, this);
30873         this.field.on("focus", function(){this.dom.select();});
30874         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30875         this.field.setHeight(18);
30876         //this.addSeparator();
30877         this.next = this.addButton({
30878             tooltip: this.nextText,
30879             cls: "x-btn-icon x-grid-page-next",
30880             disabled: true,
30881             handler: this.onClick.createDelegate(this, ["next"])
30882         });
30883         this.last = this.addButton({
30884             tooltip: this.lastText,
30885             cls: "x-btn-icon x-grid-page-last",
30886             disabled: true,
30887             handler: this.onClick.createDelegate(this, ["last"])
30888         });
30889         //this.addSeparator();
30890         this.loading = this.addButton({
30891             tooltip: this.refreshText,
30892             cls: "x-btn-icon x-grid-loading",
30893             handler: this.onClick.createDelegate(this, ["refresh"])
30894         });
30895
30896         if(this.displayInfo){
30897             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30898         }
30899     },
30900
30901     // private
30902     updateInfo : function(){
30903         if(this.displayEl){
30904             var count = this.ds.getCount();
30905             var msg = count == 0 ?
30906                 this.emptyMsg :
30907                 String.format(
30908                     this.displayMsg,
30909                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30910                 );
30911             this.displayEl.update(msg);
30912         }
30913     },
30914
30915     // private
30916     onLoad : function(ds, r, o){
30917        this.cursor = o.params ? o.params.start : 0;
30918        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30919
30920        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30921        this.field.dom.value = ap;
30922        this.first.setDisabled(ap == 1);
30923        this.prev.setDisabled(ap == 1);
30924        this.next.setDisabled(ap == ps);
30925        this.last.setDisabled(ap == ps);
30926        this.loading.enable();
30927        this.updateInfo();
30928     },
30929
30930     // private
30931     getPageData : function(){
30932         var total = this.ds.getTotalCount();
30933         return {
30934             total : total,
30935             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30936             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30937         };
30938     },
30939
30940     // private
30941     onLoadError : function(){
30942         this.loading.enable();
30943     },
30944
30945     // private
30946     onPagingKeydown : function(e){
30947         var k = e.getKey();
30948         var d = this.getPageData();
30949         if(k == e.RETURN){
30950             var v = this.field.dom.value, pageNum;
30951             if(!v || isNaN(pageNum = parseInt(v, 10))){
30952                 this.field.dom.value = d.activePage;
30953                 return;
30954             }
30955             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
30956             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30957             e.stopEvent();
30958         }
30959         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))
30960         {
30961           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
30962           this.field.dom.value = pageNum;
30963           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
30964           e.stopEvent();
30965         }
30966         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
30967         {
30968           var v = this.field.dom.value, pageNum; 
30969           var increment = (e.shiftKey) ? 10 : 1;
30970           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
30971             increment *= -1;
30972           }
30973           if(!v || isNaN(pageNum = parseInt(v, 10))) {
30974             this.field.dom.value = d.activePage;
30975             return;
30976           }
30977           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
30978           {
30979             this.field.dom.value = parseInt(v, 10) + increment;
30980             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
30981             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
30982           }
30983           e.stopEvent();
30984         }
30985     },
30986
30987     // private
30988     beforeLoad : function(){
30989         if(this.loading){
30990             this.loading.disable();
30991         }
30992     },
30993
30994     // private
30995     onClick : function(which){
30996         var ds = this.ds;
30997         switch(which){
30998             case "first":
30999                 ds.load({params:{start: 0, limit: this.pageSize}});
31000             break;
31001             case "prev":
31002                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31003             break;
31004             case "next":
31005                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31006             break;
31007             case "last":
31008                 var total = ds.getTotalCount();
31009                 var extra = total % this.pageSize;
31010                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31011                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31012             break;
31013             case "refresh":
31014                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31015             break;
31016         }
31017     },
31018
31019     /**
31020      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31021      * @param {Roo.data.Store} store The data store to unbind
31022      */
31023     unbind : function(ds){
31024         ds.un("beforeload", this.beforeLoad, this);
31025         ds.un("load", this.onLoad, this);
31026         ds.un("loadexception", this.onLoadError, this);
31027         ds.un("remove", this.updateInfo, this);
31028         ds.un("add", this.updateInfo, this);
31029         this.ds = undefined;
31030     },
31031
31032     /**
31033      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31034      * @param {Roo.data.Store} store The data store to bind
31035      */
31036     bind : function(ds){
31037         ds.on("beforeload", this.beforeLoad, this);
31038         ds.on("load", this.onLoad, this);
31039         ds.on("loadexception", this.onLoadError, this);
31040         ds.on("remove", this.updateInfo, this);
31041         ds.on("add", this.updateInfo, this);
31042         this.ds = ds;
31043     }
31044 });/*
31045  * Based on:
31046  * Ext JS Library 1.1.1
31047  * Copyright(c) 2006-2007, Ext JS, LLC.
31048  *
31049  * Originally Released Under LGPL - original licence link has changed is not relivant.
31050  *
31051  * Fork - LGPL
31052  * <script type="text/javascript">
31053  */
31054
31055 /**
31056  * @class Roo.Resizable
31057  * @extends Roo.util.Observable
31058  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31059  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31060  * 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
31061  * the element will be wrapped for you automatically.</p>
31062  * <p>Here is the list of valid resize handles:</p>
31063  * <pre>
31064 Value   Description
31065 ------  -------------------
31066  'n'     north
31067  's'     south
31068  'e'     east
31069  'w'     west
31070  'nw'    northwest
31071  'sw'    southwest
31072  'se'    southeast
31073  'ne'    northeast
31074  'hd'    horizontal drag
31075  'all'   all
31076 </pre>
31077  * <p>Here's an example showing the creation of a typical Resizable:</p>
31078  * <pre><code>
31079 var resizer = new Roo.Resizable("element-id", {
31080     handles: 'all',
31081     minWidth: 200,
31082     minHeight: 100,
31083     maxWidth: 500,
31084     maxHeight: 400,
31085     pinned: true
31086 });
31087 resizer.on("resize", myHandler);
31088 </code></pre>
31089  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31090  * resizer.east.setDisplayed(false);</p>
31091  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31092  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31093  * resize operation's new size (defaults to [0, 0])
31094  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31095  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31096  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31097  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31098  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31099  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31100  * @cfg {Number} width The width of the element in pixels (defaults to null)
31101  * @cfg {Number} height The height of the element in pixels (defaults to null)
31102  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31103  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31104  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31105  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31106  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31107  * in favor of the handles config option (defaults to false)
31108  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31109  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31110  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31111  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31112  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31113  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31114  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31115  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31116  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31117  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31118  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31119  * @constructor
31120  * Create a new resizable component
31121  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31122  * @param {Object} config configuration options
31123   */
31124 Roo.Resizable = function(el, config)
31125 {
31126     this.el = Roo.get(el);
31127
31128     if(config && config.wrap){
31129         config.resizeChild = this.el;
31130         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31131         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31132         this.el.setStyle("overflow", "hidden");
31133         this.el.setPositioning(config.resizeChild.getPositioning());
31134         config.resizeChild.clearPositioning();
31135         if(!config.width || !config.height){
31136             var csize = config.resizeChild.getSize();
31137             this.el.setSize(csize.width, csize.height);
31138         }
31139         if(config.pinned && !config.adjustments){
31140             config.adjustments = "auto";
31141         }
31142     }
31143
31144     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31145     this.proxy.unselectable();
31146     this.proxy.enableDisplayMode('block');
31147
31148     Roo.apply(this, config);
31149
31150     if(this.pinned){
31151         this.disableTrackOver = true;
31152         this.el.addClass("x-resizable-pinned");
31153     }
31154     // if the element isn't positioned, make it relative
31155     var position = this.el.getStyle("position");
31156     if(position != "absolute" && position != "fixed"){
31157         this.el.setStyle("position", "relative");
31158     }
31159     if(!this.handles){ // no handles passed, must be legacy style
31160         this.handles = 's,e,se';
31161         if(this.multiDirectional){
31162             this.handles += ',n,w';
31163         }
31164     }
31165     if(this.handles == "all"){
31166         this.handles = "n s e w ne nw se sw";
31167     }
31168     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31169     var ps = Roo.Resizable.positions;
31170     for(var i = 0, len = hs.length; i < len; i++){
31171         if(hs[i] && ps[hs[i]]){
31172             var pos = ps[hs[i]];
31173             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31174         }
31175     }
31176     // legacy
31177     this.corner = this.southeast;
31178     
31179     // updateBox = the box can move..
31180     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31181         this.updateBox = true;
31182     }
31183
31184     this.activeHandle = null;
31185
31186     if(this.resizeChild){
31187         if(typeof this.resizeChild == "boolean"){
31188             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31189         }else{
31190             this.resizeChild = Roo.get(this.resizeChild, true);
31191         }
31192     }
31193     
31194     if(this.adjustments == "auto"){
31195         var rc = this.resizeChild;
31196         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31197         if(rc && (hw || hn)){
31198             rc.position("relative");
31199             rc.setLeft(hw ? hw.el.getWidth() : 0);
31200             rc.setTop(hn ? hn.el.getHeight() : 0);
31201         }
31202         this.adjustments = [
31203             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31204             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31205         ];
31206     }
31207
31208     if(this.draggable){
31209         this.dd = this.dynamic ?
31210             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31211         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31212     }
31213
31214     // public events
31215     this.addEvents({
31216         /**
31217          * @event beforeresize
31218          * Fired before resize is allowed. Set enabled to false to cancel resize.
31219          * @param {Roo.Resizable} this
31220          * @param {Roo.EventObject} e The mousedown event
31221          */
31222         "beforeresize" : true,
31223         /**
31224          * @event resizing
31225          * Fired a resizing.
31226          * @param {Roo.Resizable} this
31227          * @param {Number} x The new x position
31228          * @param {Number} y The new y position
31229          * @param {Number} w The new w width
31230          * @param {Number} h The new h hight
31231          * @param {Roo.EventObject} e The mouseup event
31232          */
31233         "resizing" : true,
31234         /**
31235          * @event resize
31236          * Fired after a resize.
31237          * @param {Roo.Resizable} this
31238          * @param {Number} width The new width
31239          * @param {Number} height The new height
31240          * @param {Roo.EventObject} e The mouseup event
31241          */
31242         "resize" : true
31243     });
31244
31245     if(this.width !== null && this.height !== null){
31246         this.resizeTo(this.width, this.height);
31247     }else{
31248         this.updateChildSize();
31249     }
31250     if(Roo.isIE){
31251         this.el.dom.style.zoom = 1;
31252     }
31253     Roo.Resizable.superclass.constructor.call(this);
31254 };
31255
31256 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31257         resizeChild : false,
31258         adjustments : [0, 0],
31259         minWidth : 5,
31260         minHeight : 5,
31261         maxWidth : 10000,
31262         maxHeight : 10000,
31263         enabled : true,
31264         animate : false,
31265         duration : .35,
31266         dynamic : false,
31267         handles : false,
31268         multiDirectional : false,
31269         disableTrackOver : false,
31270         easing : 'easeOutStrong',
31271         widthIncrement : 0,
31272         heightIncrement : 0,
31273         pinned : false,
31274         width : null,
31275         height : null,
31276         preserveRatio : false,
31277         transparent: false,
31278         minX: 0,
31279         minY: 0,
31280         draggable: false,
31281
31282         /**
31283          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31284          */
31285         constrainTo: undefined,
31286         /**
31287          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31288          */
31289         resizeRegion: undefined,
31290
31291
31292     /**
31293      * Perform a manual resize
31294      * @param {Number} width
31295      * @param {Number} height
31296      */
31297     resizeTo : function(width, height){
31298         this.el.setSize(width, height);
31299         this.updateChildSize();
31300         this.fireEvent("resize", this, width, height, null);
31301     },
31302
31303     // private
31304     startSizing : function(e, handle){
31305         this.fireEvent("beforeresize", this, e);
31306         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31307
31308             if(!this.overlay){
31309                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31310                 this.overlay.unselectable();
31311                 this.overlay.enableDisplayMode("block");
31312                 this.overlay.on("mousemove", this.onMouseMove, this);
31313                 this.overlay.on("mouseup", this.onMouseUp, this);
31314             }
31315             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31316
31317             this.resizing = true;
31318             this.startBox = this.el.getBox();
31319             this.startPoint = e.getXY();
31320             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31321                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31322
31323             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31324             this.overlay.show();
31325
31326             if(this.constrainTo) {
31327                 var ct = Roo.get(this.constrainTo);
31328                 this.resizeRegion = ct.getRegion().adjust(
31329                     ct.getFrameWidth('t'),
31330                     ct.getFrameWidth('l'),
31331                     -ct.getFrameWidth('b'),
31332                     -ct.getFrameWidth('r')
31333                 );
31334             }
31335
31336             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31337             this.proxy.show();
31338             this.proxy.setBox(this.startBox);
31339             if(!this.dynamic){
31340                 this.proxy.setStyle('visibility', 'visible');
31341             }
31342         }
31343     },
31344
31345     // private
31346     onMouseDown : function(handle, e){
31347         if(this.enabled){
31348             e.stopEvent();
31349             this.activeHandle = handle;
31350             this.startSizing(e, handle);
31351         }
31352     },
31353
31354     // private
31355     onMouseUp : function(e){
31356         var size = this.resizeElement();
31357         this.resizing = false;
31358         this.handleOut();
31359         this.overlay.hide();
31360         this.proxy.hide();
31361         this.fireEvent("resize", this, size.width, size.height, e);
31362     },
31363
31364     // private
31365     updateChildSize : function(){
31366         
31367         if(this.resizeChild){
31368             var el = this.el;
31369             var child = this.resizeChild;
31370             var adj = this.adjustments;
31371             if(el.dom.offsetWidth){
31372                 var b = el.getSize(true);
31373                 child.setSize(b.width+adj[0], b.height+adj[1]);
31374             }
31375             // Second call here for IE
31376             // The first call enables instant resizing and
31377             // the second call corrects scroll bars if they
31378             // exist
31379             if(Roo.isIE){
31380                 setTimeout(function(){
31381                     if(el.dom.offsetWidth){
31382                         var b = el.getSize(true);
31383                         child.setSize(b.width+adj[0], b.height+adj[1]);
31384                     }
31385                 }, 10);
31386             }
31387         }
31388     },
31389
31390     // private
31391     snap : function(value, inc, min){
31392         if(!inc || !value) {
31393             return value;
31394         }
31395         var newValue = value;
31396         var m = value % inc;
31397         if(m > 0){
31398             if(m > (inc/2)){
31399                 newValue = value + (inc-m);
31400             }else{
31401                 newValue = value - m;
31402             }
31403         }
31404         return Math.max(min, newValue);
31405     },
31406
31407     // private
31408     resizeElement : function(){
31409         var box = this.proxy.getBox();
31410         if(this.updateBox){
31411             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31412         }else{
31413             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31414         }
31415         this.updateChildSize();
31416         if(!this.dynamic){
31417             this.proxy.hide();
31418         }
31419         return box;
31420     },
31421
31422     // private
31423     constrain : function(v, diff, m, mx){
31424         if(v - diff < m){
31425             diff = v - m;
31426         }else if(v - diff > mx){
31427             diff = mx - v;
31428         }
31429         return diff;
31430     },
31431
31432     // private
31433     onMouseMove : function(e){
31434         
31435         if(this.enabled){
31436             try{// try catch so if something goes wrong the user doesn't get hung
31437
31438             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31439                 return;
31440             }
31441
31442             //var curXY = this.startPoint;
31443             var curSize = this.curSize || this.startBox;
31444             var x = this.startBox.x, y = this.startBox.y;
31445             var ox = x, oy = y;
31446             var w = curSize.width, h = curSize.height;
31447             var ow = w, oh = h;
31448             var mw = this.minWidth, mh = this.minHeight;
31449             var mxw = this.maxWidth, mxh = this.maxHeight;
31450             var wi = this.widthIncrement;
31451             var hi = this.heightIncrement;
31452
31453             var eventXY = e.getXY();
31454             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31455             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31456
31457             var pos = this.activeHandle.position;
31458
31459             switch(pos){
31460                 case "east":
31461                     w += diffX;
31462                     w = Math.min(Math.max(mw, w), mxw);
31463                     break;
31464              
31465                 case "south":
31466                     h += diffY;
31467                     h = Math.min(Math.max(mh, h), mxh);
31468                     break;
31469                 case "southeast":
31470                     w += diffX;
31471                     h += diffY;
31472                     w = Math.min(Math.max(mw, w), mxw);
31473                     h = Math.min(Math.max(mh, h), mxh);
31474                     break;
31475                 case "north":
31476                     diffY = this.constrain(h, diffY, mh, mxh);
31477                     y += diffY;
31478                     h -= diffY;
31479                     break;
31480                 case "hdrag":
31481                     
31482                     if (wi) {
31483                         var adiffX = Math.abs(diffX);
31484                         var sub = (adiffX % wi); // how much 
31485                         if (sub > (wi/2)) { // far enough to snap
31486                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31487                         } else {
31488                             // remove difference.. 
31489                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31490                         }
31491                     }
31492                     x += diffX;
31493                     x = Math.max(this.minX, x);
31494                     break;
31495                 case "west":
31496                     diffX = this.constrain(w, diffX, mw, mxw);
31497                     x += diffX;
31498                     w -= diffX;
31499                     break;
31500                 case "northeast":
31501                     w += diffX;
31502                     w = Math.min(Math.max(mw, w), mxw);
31503                     diffY = this.constrain(h, diffY, mh, mxh);
31504                     y += diffY;
31505                     h -= diffY;
31506                     break;
31507                 case "northwest":
31508                     diffX = this.constrain(w, diffX, mw, mxw);
31509                     diffY = this.constrain(h, diffY, mh, mxh);
31510                     y += diffY;
31511                     h -= diffY;
31512                     x += diffX;
31513                     w -= diffX;
31514                     break;
31515                case "southwest":
31516                     diffX = this.constrain(w, diffX, mw, mxw);
31517                     h += diffY;
31518                     h = Math.min(Math.max(mh, h), mxh);
31519                     x += diffX;
31520                     w -= diffX;
31521                     break;
31522             }
31523
31524             var sw = this.snap(w, wi, mw);
31525             var sh = this.snap(h, hi, mh);
31526             if(sw != w || sh != h){
31527                 switch(pos){
31528                     case "northeast":
31529                         y -= sh - h;
31530                     break;
31531                     case "north":
31532                         y -= sh - h;
31533                         break;
31534                     case "southwest":
31535                         x -= sw - w;
31536                     break;
31537                     case "west":
31538                         x -= sw - w;
31539                         break;
31540                     case "northwest":
31541                         x -= sw - w;
31542                         y -= sh - h;
31543                     break;
31544                 }
31545                 w = sw;
31546                 h = sh;
31547             }
31548
31549             if(this.preserveRatio){
31550                 switch(pos){
31551                     case "southeast":
31552                     case "east":
31553                         h = oh * (w/ow);
31554                         h = Math.min(Math.max(mh, h), mxh);
31555                         w = ow * (h/oh);
31556                        break;
31557                     case "south":
31558                         w = ow * (h/oh);
31559                         w = Math.min(Math.max(mw, w), mxw);
31560                         h = oh * (w/ow);
31561                         break;
31562                     case "northeast":
31563                         w = ow * (h/oh);
31564                         w = Math.min(Math.max(mw, w), mxw);
31565                         h = oh * (w/ow);
31566                     break;
31567                     case "north":
31568                         var tw = w;
31569                         w = ow * (h/oh);
31570                         w = Math.min(Math.max(mw, w), mxw);
31571                         h = oh * (w/ow);
31572                         x += (tw - w) / 2;
31573                         break;
31574                     case "southwest":
31575                         h = oh * (w/ow);
31576                         h = Math.min(Math.max(mh, h), mxh);
31577                         var tw = w;
31578                         w = ow * (h/oh);
31579                         x += tw - w;
31580                         break;
31581                     case "west":
31582                         var th = h;
31583                         h = oh * (w/ow);
31584                         h = Math.min(Math.max(mh, h), mxh);
31585                         y += (th - h) / 2;
31586                         var tw = w;
31587                         w = ow * (h/oh);
31588                         x += tw - w;
31589                        break;
31590                     case "northwest":
31591                         var tw = w;
31592                         var th = h;
31593                         h = oh * (w/ow);
31594                         h = Math.min(Math.max(mh, h), mxh);
31595                         w = ow * (h/oh);
31596                         y += th - h;
31597                         x += tw - w;
31598                        break;
31599
31600                 }
31601             }
31602             if (pos == 'hdrag') {
31603                 w = ow;
31604             }
31605             this.proxy.setBounds(x, y, w, h);
31606             if(this.dynamic){
31607                 this.resizeElement();
31608             }
31609             }catch(e){}
31610         }
31611         this.fireEvent("resizing", this, x, y, w, h, e);
31612     },
31613
31614     // private
31615     handleOver : function(){
31616         if(this.enabled){
31617             this.el.addClass("x-resizable-over");
31618         }
31619     },
31620
31621     // private
31622     handleOut : function(){
31623         if(!this.resizing){
31624             this.el.removeClass("x-resizable-over");
31625         }
31626     },
31627
31628     /**
31629      * Returns the element this component is bound to.
31630      * @return {Roo.Element}
31631      */
31632     getEl : function(){
31633         return this.el;
31634     },
31635
31636     /**
31637      * Returns the resizeChild element (or null).
31638      * @return {Roo.Element}
31639      */
31640     getResizeChild : function(){
31641         return this.resizeChild;
31642     },
31643     groupHandler : function()
31644     {
31645         
31646     },
31647     /**
31648      * Destroys this resizable. If the element was wrapped and
31649      * removeEl is not true then the element remains.
31650      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31651      */
31652     destroy : function(removeEl){
31653         this.proxy.remove();
31654         if(this.overlay){
31655             this.overlay.removeAllListeners();
31656             this.overlay.remove();
31657         }
31658         var ps = Roo.Resizable.positions;
31659         for(var k in ps){
31660             if(typeof ps[k] != "function" && this[ps[k]]){
31661                 var h = this[ps[k]];
31662                 h.el.removeAllListeners();
31663                 h.el.remove();
31664             }
31665         }
31666         if(removeEl){
31667             this.el.update("");
31668             this.el.remove();
31669         }
31670     }
31671 });
31672
31673 // private
31674 // hash to map config positions to true positions
31675 Roo.Resizable.positions = {
31676     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31677     hd: "hdrag"
31678 };
31679
31680 // private
31681 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31682     if(!this.tpl){
31683         // only initialize the template if resizable is used
31684         var tpl = Roo.DomHelper.createTemplate(
31685             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31686         );
31687         tpl.compile();
31688         Roo.Resizable.Handle.prototype.tpl = tpl;
31689     }
31690     this.position = pos;
31691     this.rz = rz;
31692     // show north drag fro topdra
31693     var handlepos = pos == 'hdrag' ? 'north' : pos;
31694     
31695     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31696     if (pos == 'hdrag') {
31697         this.el.setStyle('cursor', 'pointer');
31698     }
31699     this.el.unselectable();
31700     if(transparent){
31701         this.el.setOpacity(0);
31702     }
31703     this.el.on("mousedown", this.onMouseDown, this);
31704     if(!disableTrackOver){
31705         this.el.on("mouseover", this.onMouseOver, this);
31706         this.el.on("mouseout", this.onMouseOut, this);
31707     }
31708 };
31709
31710 // private
31711 Roo.Resizable.Handle.prototype = {
31712     afterResize : function(rz){
31713         Roo.log('after?');
31714         // do nothing
31715     },
31716     // private
31717     onMouseDown : function(e){
31718         this.rz.onMouseDown(this, e);
31719     },
31720     // private
31721     onMouseOver : function(e){
31722         this.rz.handleOver(this, e);
31723     },
31724     // private
31725     onMouseOut : function(e){
31726         this.rz.handleOut(this, e);
31727     }
31728 };/*
31729  * Based on:
31730  * Ext JS Library 1.1.1
31731  * Copyright(c) 2006-2007, Ext JS, LLC.
31732  *
31733  * Originally Released Under LGPL - original licence link has changed is not relivant.
31734  *
31735  * Fork - LGPL
31736  * <script type="text/javascript">
31737  */
31738
31739 /**
31740  * @class Roo.Editor
31741  * @extends Roo.Component
31742  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31743  * @constructor
31744  * Create a new Editor
31745  * @param {Roo.form.Field} field The Field object (or descendant)
31746  * @param {Object} config The config object
31747  */
31748 Roo.Editor = function(field, config){
31749     Roo.Editor.superclass.constructor.call(this, config);
31750     this.field = field;
31751     this.addEvents({
31752         /**
31753              * @event beforestartedit
31754              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31755              * false from the handler of this event.
31756              * @param {Editor} this
31757              * @param {Roo.Element} boundEl The underlying element bound to this editor
31758              * @param {Mixed} value The field value being set
31759              */
31760         "beforestartedit" : true,
31761         /**
31762              * @event startedit
31763              * Fires when this editor is displayed
31764              * @param {Roo.Element} boundEl The underlying element bound to this editor
31765              * @param {Mixed} value The starting field value
31766              */
31767         "startedit" : true,
31768         /**
31769              * @event beforecomplete
31770              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31771              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31772              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31773              * event will not fire since no edit actually occurred.
31774              * @param {Editor} this
31775              * @param {Mixed} value The current field value
31776              * @param {Mixed} startValue The original field value
31777              */
31778         "beforecomplete" : true,
31779         /**
31780              * @event complete
31781              * Fires after editing is complete and any changed value has been written to the underlying field.
31782              * @param {Editor} this
31783              * @param {Mixed} value The current field value
31784              * @param {Mixed} startValue The original field value
31785              */
31786         "complete" : true,
31787         /**
31788          * @event specialkey
31789          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31790          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31791          * @param {Roo.form.Field} this
31792          * @param {Roo.EventObject} e The event object
31793          */
31794         "specialkey" : true
31795     });
31796 };
31797
31798 Roo.extend(Roo.Editor, Roo.Component, {
31799     /**
31800      * @cfg {Boolean/String} autosize
31801      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31802      * or "height" to adopt the height only (defaults to false)
31803      */
31804     /**
31805      * @cfg {Boolean} revertInvalid
31806      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31807      * validation fails (defaults to true)
31808      */
31809     /**
31810      * @cfg {Boolean} ignoreNoChange
31811      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31812      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31813      * will never be ignored.
31814      */
31815     /**
31816      * @cfg {Boolean} hideEl
31817      * False to keep the bound element visible while the editor is displayed (defaults to true)
31818      */
31819     /**
31820      * @cfg {Mixed} value
31821      * The data value of the underlying field (defaults to "")
31822      */
31823     value : "",
31824     /**
31825      * @cfg {String} alignment
31826      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31827      */
31828     alignment: "c-c?",
31829     /**
31830      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31831      * for bottom-right shadow (defaults to "frame")
31832      */
31833     shadow : "frame",
31834     /**
31835      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31836      */
31837     constrain : false,
31838     /**
31839      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31840      */
31841     completeOnEnter : false,
31842     /**
31843      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31844      */
31845     cancelOnEsc : false,
31846     /**
31847      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31848      */
31849     updateEl : false,
31850
31851     // private
31852     onRender : function(ct, position){
31853         this.el = new Roo.Layer({
31854             shadow: this.shadow,
31855             cls: "x-editor",
31856             parentEl : ct,
31857             shim : this.shim,
31858             shadowOffset:4,
31859             id: this.id,
31860             constrain: this.constrain
31861         });
31862         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31863         if(this.field.msgTarget != 'title'){
31864             this.field.msgTarget = 'qtip';
31865         }
31866         this.field.render(this.el);
31867         if(Roo.isGecko){
31868             this.field.el.dom.setAttribute('autocomplete', 'off');
31869         }
31870         this.field.on("specialkey", this.onSpecialKey, this);
31871         if(this.swallowKeys){
31872             this.field.el.swallowEvent(['keydown','keypress']);
31873         }
31874         this.field.show();
31875         this.field.on("blur", this.onBlur, this);
31876         if(this.field.grow){
31877             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31878         }
31879     },
31880
31881     onSpecialKey : function(field, e)
31882     {
31883         //Roo.log('editor onSpecialKey');
31884         if(this.completeOnEnter && e.getKey() == e.ENTER){
31885             e.stopEvent();
31886             this.completeEdit();
31887             return;
31888         }
31889         // do not fire special key otherwise it might hide close the editor...
31890         if(e.getKey() == e.ENTER){    
31891             return;
31892         }
31893         if(this.cancelOnEsc && e.getKey() == e.ESC){
31894             this.cancelEdit();
31895             return;
31896         } 
31897         this.fireEvent('specialkey', field, e);
31898     
31899     },
31900
31901     /**
31902      * Starts the editing process and shows the editor.
31903      * @param {String/HTMLElement/Element} el The element to edit
31904      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31905       * to the innerHTML of el.
31906      */
31907     startEdit : function(el, value){
31908         if(this.editing){
31909             this.completeEdit();
31910         }
31911         this.boundEl = Roo.get(el);
31912         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31913         if(!this.rendered){
31914             this.render(this.parentEl || document.body);
31915         }
31916         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31917             return;
31918         }
31919         this.startValue = v;
31920         this.field.setValue(v);
31921         if(this.autoSize){
31922             var sz = this.boundEl.getSize();
31923             switch(this.autoSize){
31924                 case "width":
31925                 this.setSize(sz.width,  "");
31926                 break;
31927                 case "height":
31928                 this.setSize("",  sz.height);
31929                 break;
31930                 default:
31931                 this.setSize(sz.width,  sz.height);
31932             }
31933         }
31934         this.el.alignTo(this.boundEl, this.alignment);
31935         this.editing = true;
31936         if(Roo.QuickTips){
31937             Roo.QuickTips.disable();
31938         }
31939         this.show();
31940     },
31941
31942     /**
31943      * Sets the height and width of this editor.
31944      * @param {Number} width The new width
31945      * @param {Number} height The new height
31946      */
31947     setSize : function(w, h){
31948         this.field.setSize(w, h);
31949         if(this.el){
31950             this.el.sync();
31951         }
31952     },
31953
31954     /**
31955      * Realigns the editor to the bound field based on the current alignment config value.
31956      */
31957     realign : function(){
31958         this.el.alignTo(this.boundEl, this.alignment);
31959     },
31960
31961     /**
31962      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
31963      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
31964      */
31965     completeEdit : function(remainVisible){
31966         if(!this.editing){
31967             return;
31968         }
31969         var v = this.getValue();
31970         if(this.revertInvalid !== false && !this.field.isValid()){
31971             v = this.startValue;
31972             this.cancelEdit(true);
31973         }
31974         if(String(v) === String(this.startValue) && this.ignoreNoChange){
31975             this.editing = false;
31976             this.hide();
31977             return;
31978         }
31979         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
31980             this.editing = false;
31981             if(this.updateEl && this.boundEl){
31982                 this.boundEl.update(v);
31983             }
31984             if(remainVisible !== true){
31985                 this.hide();
31986             }
31987             this.fireEvent("complete", this, v, this.startValue);
31988         }
31989     },
31990
31991     // private
31992     onShow : function(){
31993         this.el.show();
31994         if(this.hideEl !== false){
31995             this.boundEl.hide();
31996         }
31997         this.field.show();
31998         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
31999             this.fixIEFocus = true;
32000             this.deferredFocus.defer(50, this);
32001         }else{
32002             this.field.focus();
32003         }
32004         this.fireEvent("startedit", this.boundEl, this.startValue);
32005     },
32006
32007     deferredFocus : function(){
32008         if(this.editing){
32009             this.field.focus();
32010         }
32011     },
32012
32013     /**
32014      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32015      * reverted to the original starting value.
32016      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32017      * cancel (defaults to false)
32018      */
32019     cancelEdit : function(remainVisible){
32020         if(this.editing){
32021             this.setValue(this.startValue);
32022             if(remainVisible !== true){
32023                 this.hide();
32024             }
32025         }
32026     },
32027
32028     // private
32029     onBlur : function(){
32030         if(this.allowBlur !== true && this.editing){
32031             this.completeEdit();
32032         }
32033     },
32034
32035     // private
32036     onHide : function(){
32037         if(this.editing){
32038             this.completeEdit();
32039             return;
32040         }
32041         this.field.blur();
32042         if(this.field.collapse){
32043             this.field.collapse();
32044         }
32045         this.el.hide();
32046         if(this.hideEl !== false){
32047             this.boundEl.show();
32048         }
32049         if(Roo.QuickTips){
32050             Roo.QuickTips.enable();
32051         }
32052     },
32053
32054     /**
32055      * Sets the data value of the editor
32056      * @param {Mixed} value Any valid value supported by the underlying field
32057      */
32058     setValue : function(v){
32059         this.field.setValue(v);
32060     },
32061
32062     /**
32063      * Gets the data value of the editor
32064      * @return {Mixed} The data value
32065      */
32066     getValue : function(){
32067         return this.field.getValue();
32068     }
32069 });/*
32070  * Based on:
32071  * Ext JS Library 1.1.1
32072  * Copyright(c) 2006-2007, Ext JS, LLC.
32073  *
32074  * Originally Released Under LGPL - original licence link has changed is not relivant.
32075  *
32076  * Fork - LGPL
32077  * <script type="text/javascript">
32078  */
32079  
32080 /**
32081  * @class Roo.BasicDialog
32082  * @extends Roo.util.Observable
32083  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32084  * <pre><code>
32085 var dlg = new Roo.BasicDialog("my-dlg", {
32086     height: 200,
32087     width: 300,
32088     minHeight: 100,
32089     minWidth: 150,
32090     modal: true,
32091     proxyDrag: true,
32092     shadow: true
32093 });
32094 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32095 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32096 dlg.addButton('Cancel', dlg.hide, dlg);
32097 dlg.show();
32098 </code></pre>
32099   <b>A Dialog should always be a direct child of the body element.</b>
32100  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32101  * @cfg {String} title Default text to display in the title bar (defaults to null)
32102  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32103  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32104  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32105  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32106  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32107  * (defaults to null with no animation)
32108  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32109  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32110  * property for valid values (defaults to 'all')
32111  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32112  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32113  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32114  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32115  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32116  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32117  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32118  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32119  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32120  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32121  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32122  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32123  * draggable = true (defaults to false)
32124  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32125  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32126  * shadow (defaults to false)
32127  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32128  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32129  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32130  * @cfg {Array} buttons Array of buttons
32131  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32132  * @constructor
32133  * Create a new BasicDialog.
32134  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32135  * @param {Object} config Configuration options
32136  */
32137 Roo.BasicDialog = function(el, config){
32138     this.el = Roo.get(el);
32139     var dh = Roo.DomHelper;
32140     if(!this.el && config && config.autoCreate){
32141         if(typeof config.autoCreate == "object"){
32142             if(!config.autoCreate.id){
32143                 config.autoCreate.id = el;
32144             }
32145             this.el = dh.append(document.body,
32146                         config.autoCreate, true);
32147         }else{
32148             this.el = dh.append(document.body,
32149                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32150         }
32151     }
32152     el = this.el;
32153     el.setDisplayed(true);
32154     el.hide = this.hideAction;
32155     this.id = el.id;
32156     el.addClass("x-dlg");
32157
32158     Roo.apply(this, config);
32159
32160     this.proxy = el.createProxy("x-dlg-proxy");
32161     this.proxy.hide = this.hideAction;
32162     this.proxy.setOpacity(.5);
32163     this.proxy.hide();
32164
32165     if(config.width){
32166         el.setWidth(config.width);
32167     }
32168     if(config.height){
32169         el.setHeight(config.height);
32170     }
32171     this.size = el.getSize();
32172     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32173         this.xy = [config.x,config.y];
32174     }else{
32175         this.xy = el.getCenterXY(true);
32176     }
32177     /** The header element @type Roo.Element */
32178     this.header = el.child("> .x-dlg-hd");
32179     /** The body element @type Roo.Element */
32180     this.body = el.child("> .x-dlg-bd");
32181     /** The footer element @type Roo.Element */
32182     this.footer = el.child("> .x-dlg-ft");
32183
32184     if(!this.header){
32185         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32186     }
32187     if(!this.body){
32188         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32189     }
32190
32191     this.header.unselectable();
32192     if(this.title){
32193         this.header.update(this.title);
32194     }
32195     // this element allows the dialog to be focused for keyboard event
32196     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32197     this.focusEl.swallowEvent("click", true);
32198
32199     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32200
32201     // wrap the body and footer for special rendering
32202     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32203     if(this.footer){
32204         this.bwrap.dom.appendChild(this.footer.dom);
32205     }
32206
32207     this.bg = this.el.createChild({
32208         tag: "div", cls:"x-dlg-bg",
32209         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32210     });
32211     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32212
32213
32214     if(this.autoScroll !== false && !this.autoTabs){
32215         this.body.setStyle("overflow", "auto");
32216     }
32217
32218     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32219
32220     if(this.closable !== false){
32221         this.el.addClass("x-dlg-closable");
32222         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32223         this.close.on("click", this.closeClick, this);
32224         this.close.addClassOnOver("x-dlg-close-over");
32225     }
32226     if(this.collapsible !== false){
32227         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32228         this.collapseBtn.on("click", this.collapseClick, this);
32229         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32230         this.header.on("dblclick", this.collapseClick, this);
32231     }
32232     if(this.resizable !== false){
32233         this.el.addClass("x-dlg-resizable");
32234         this.resizer = new Roo.Resizable(el, {
32235             minWidth: this.minWidth || 80,
32236             minHeight:this.minHeight || 80,
32237             handles: this.resizeHandles || "all",
32238             pinned: true
32239         });
32240         this.resizer.on("beforeresize", this.beforeResize, this);
32241         this.resizer.on("resize", this.onResize, this);
32242     }
32243     if(this.draggable !== false){
32244         el.addClass("x-dlg-draggable");
32245         if (!this.proxyDrag) {
32246             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32247         }
32248         else {
32249             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32250         }
32251         dd.setHandleElId(this.header.id);
32252         dd.endDrag = this.endMove.createDelegate(this);
32253         dd.startDrag = this.startMove.createDelegate(this);
32254         dd.onDrag = this.onDrag.createDelegate(this);
32255         dd.scroll = false;
32256         this.dd = dd;
32257     }
32258     if(this.modal){
32259         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32260         this.mask.enableDisplayMode("block");
32261         this.mask.hide();
32262         this.el.addClass("x-dlg-modal");
32263     }
32264     if(this.shadow){
32265         this.shadow = new Roo.Shadow({
32266             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32267             offset : this.shadowOffset
32268         });
32269     }else{
32270         this.shadowOffset = 0;
32271     }
32272     if(Roo.useShims && this.shim !== false){
32273         this.shim = this.el.createShim();
32274         this.shim.hide = this.hideAction;
32275         this.shim.hide();
32276     }else{
32277         this.shim = false;
32278     }
32279     if(this.autoTabs){
32280         this.initTabs();
32281     }
32282     if (this.buttons) { 
32283         var bts= this.buttons;
32284         this.buttons = [];
32285         Roo.each(bts, function(b) {
32286             this.addButton(b);
32287         }, this);
32288     }
32289     
32290     
32291     this.addEvents({
32292         /**
32293          * @event keydown
32294          * Fires when a key is pressed
32295          * @param {Roo.BasicDialog} this
32296          * @param {Roo.EventObject} e
32297          */
32298         "keydown" : true,
32299         /**
32300          * @event move
32301          * Fires when this dialog is moved by the user.
32302          * @param {Roo.BasicDialog} this
32303          * @param {Number} x The new page X
32304          * @param {Number} y The new page Y
32305          */
32306         "move" : true,
32307         /**
32308          * @event resize
32309          * Fires when this dialog is resized by the user.
32310          * @param {Roo.BasicDialog} this
32311          * @param {Number} width The new width
32312          * @param {Number} height The new height
32313          */
32314         "resize" : true,
32315         /**
32316          * @event beforehide
32317          * Fires before this dialog is hidden.
32318          * @param {Roo.BasicDialog} this
32319          */
32320         "beforehide" : true,
32321         /**
32322          * @event hide
32323          * Fires when this dialog is hidden.
32324          * @param {Roo.BasicDialog} this
32325          */
32326         "hide" : true,
32327         /**
32328          * @event beforeshow
32329          * Fires before this dialog is shown.
32330          * @param {Roo.BasicDialog} this
32331          */
32332         "beforeshow" : true,
32333         /**
32334          * @event show
32335          * Fires when this dialog is shown.
32336          * @param {Roo.BasicDialog} this
32337          */
32338         "show" : true
32339     });
32340     el.on("keydown", this.onKeyDown, this);
32341     el.on("mousedown", this.toFront, this);
32342     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32343     this.el.hide();
32344     Roo.DialogManager.register(this);
32345     Roo.BasicDialog.superclass.constructor.call(this);
32346 };
32347
32348 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32349     shadowOffset: Roo.isIE ? 6 : 5,
32350     minHeight: 80,
32351     minWidth: 200,
32352     minButtonWidth: 75,
32353     defaultButton: null,
32354     buttonAlign: "right",
32355     tabTag: 'div',
32356     firstShow: true,
32357
32358     /**
32359      * Sets the dialog title text
32360      * @param {String} text The title text to display
32361      * @return {Roo.BasicDialog} this
32362      */
32363     setTitle : function(text){
32364         this.header.update(text);
32365         return this;
32366     },
32367
32368     // private
32369     closeClick : function(){
32370         this.hide();
32371     },
32372
32373     // private
32374     collapseClick : function(){
32375         this[this.collapsed ? "expand" : "collapse"]();
32376     },
32377
32378     /**
32379      * Collapses the dialog to its minimized state (only the title bar is visible).
32380      * Equivalent to the user clicking the collapse dialog button.
32381      */
32382     collapse : function(){
32383         if(!this.collapsed){
32384             this.collapsed = true;
32385             this.el.addClass("x-dlg-collapsed");
32386             this.restoreHeight = this.el.getHeight();
32387             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32388         }
32389     },
32390
32391     /**
32392      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32393      * clicking the expand dialog button.
32394      */
32395     expand : function(){
32396         if(this.collapsed){
32397             this.collapsed = false;
32398             this.el.removeClass("x-dlg-collapsed");
32399             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32400         }
32401     },
32402
32403     /**
32404      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32405      * @return {Roo.TabPanel} The tabs component
32406      */
32407     initTabs : function(){
32408         var tabs = this.getTabs();
32409         while(tabs.getTab(0)){
32410             tabs.removeTab(0);
32411         }
32412         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32413             var dom = el.dom;
32414             tabs.addTab(Roo.id(dom), dom.title);
32415             dom.title = "";
32416         });
32417         tabs.activate(0);
32418         return tabs;
32419     },
32420
32421     // private
32422     beforeResize : function(){
32423         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32424     },
32425
32426     // private
32427     onResize : function(){
32428         this.refreshSize();
32429         this.syncBodyHeight();
32430         this.adjustAssets();
32431         this.focus();
32432         this.fireEvent("resize", this, this.size.width, this.size.height);
32433     },
32434
32435     // private
32436     onKeyDown : function(e){
32437         if(this.isVisible()){
32438             this.fireEvent("keydown", this, e);
32439         }
32440     },
32441
32442     /**
32443      * Resizes the dialog.
32444      * @param {Number} width
32445      * @param {Number} height
32446      * @return {Roo.BasicDialog} this
32447      */
32448     resizeTo : function(width, height){
32449         this.el.setSize(width, height);
32450         this.size = {width: width, height: height};
32451         this.syncBodyHeight();
32452         if(this.fixedcenter){
32453             this.center();
32454         }
32455         if(this.isVisible()){
32456             this.constrainXY();
32457             this.adjustAssets();
32458         }
32459         this.fireEvent("resize", this, width, height);
32460         return this;
32461     },
32462
32463
32464     /**
32465      * Resizes the dialog to fit the specified content size.
32466      * @param {Number} width
32467      * @param {Number} height
32468      * @return {Roo.BasicDialog} this
32469      */
32470     setContentSize : function(w, h){
32471         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32472         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32473         //if(!this.el.isBorderBox()){
32474             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32475             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32476         //}
32477         if(this.tabs){
32478             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32479             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32480         }
32481         this.resizeTo(w, h);
32482         return this;
32483     },
32484
32485     /**
32486      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32487      * executed in response to a particular key being pressed while the dialog is active.
32488      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32489      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32490      * @param {Function} fn The function to call
32491      * @param {Object} scope (optional) The scope of the function
32492      * @return {Roo.BasicDialog} this
32493      */
32494     addKeyListener : function(key, fn, scope){
32495         var keyCode, shift, ctrl, alt;
32496         if(typeof key == "object" && !(key instanceof Array)){
32497             keyCode = key["key"];
32498             shift = key["shift"];
32499             ctrl = key["ctrl"];
32500             alt = key["alt"];
32501         }else{
32502             keyCode = key;
32503         }
32504         var handler = function(dlg, e){
32505             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32506                 var k = e.getKey();
32507                 if(keyCode instanceof Array){
32508                     for(var i = 0, len = keyCode.length; i < len; i++){
32509                         if(keyCode[i] == k){
32510                           fn.call(scope || window, dlg, k, e);
32511                           return;
32512                         }
32513                     }
32514                 }else{
32515                     if(k == keyCode){
32516                         fn.call(scope || window, dlg, k, e);
32517                     }
32518                 }
32519             }
32520         };
32521         this.on("keydown", handler);
32522         return this;
32523     },
32524
32525     /**
32526      * Returns the TabPanel component (creates it if it doesn't exist).
32527      * Note: If you wish to simply check for the existence of tabs without creating them,
32528      * check for a null 'tabs' property.
32529      * @return {Roo.TabPanel} The tabs component
32530      */
32531     getTabs : function(){
32532         if(!this.tabs){
32533             this.el.addClass("x-dlg-auto-tabs");
32534             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32535             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32536         }
32537         return this.tabs;
32538     },
32539
32540     /**
32541      * Adds a button to the footer section of the dialog.
32542      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32543      * object or a valid Roo.DomHelper element config
32544      * @param {Function} handler The function called when the button is clicked
32545      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32546      * @return {Roo.Button} The new button
32547      */
32548     addButton : function(config, handler, scope){
32549         var dh = Roo.DomHelper;
32550         if(!this.footer){
32551             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32552         }
32553         if(!this.btnContainer){
32554             var tb = this.footer.createChild({
32555
32556                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32557                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32558             }, null, true);
32559             this.btnContainer = tb.firstChild.firstChild.firstChild;
32560         }
32561         var bconfig = {
32562             handler: handler,
32563             scope: scope,
32564             minWidth: this.minButtonWidth,
32565             hideParent:true
32566         };
32567         if(typeof config == "string"){
32568             bconfig.text = config;
32569         }else{
32570             if(config.tag){
32571                 bconfig.dhconfig = config;
32572             }else{
32573                 Roo.apply(bconfig, config);
32574             }
32575         }
32576         var fc = false;
32577         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32578             bconfig.position = Math.max(0, bconfig.position);
32579             fc = this.btnContainer.childNodes[bconfig.position];
32580         }
32581          
32582         var btn = new Roo.Button(
32583             fc ? 
32584                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32585                 : this.btnContainer.appendChild(document.createElement("td")),
32586             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32587             bconfig
32588         );
32589         this.syncBodyHeight();
32590         if(!this.buttons){
32591             /**
32592              * Array of all the buttons that have been added to this dialog via addButton
32593              * @type Array
32594              */
32595             this.buttons = [];
32596         }
32597         this.buttons.push(btn);
32598         return btn;
32599     },
32600
32601     /**
32602      * Sets the default button to be focused when the dialog is displayed.
32603      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32604      * @return {Roo.BasicDialog} this
32605      */
32606     setDefaultButton : function(btn){
32607         this.defaultButton = btn;
32608         return this;
32609     },
32610
32611     // private
32612     getHeaderFooterHeight : function(safe){
32613         var height = 0;
32614         if(this.header){
32615            height += this.header.getHeight();
32616         }
32617         if(this.footer){
32618            var fm = this.footer.getMargins();
32619             height += (this.footer.getHeight()+fm.top+fm.bottom);
32620         }
32621         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32622         height += this.centerBg.getPadding("tb");
32623         return height;
32624     },
32625
32626     // private
32627     syncBodyHeight : function()
32628     {
32629         var bd = this.body, // the text
32630             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32631             bw = this.bwrap;
32632         var height = this.size.height - this.getHeaderFooterHeight(false);
32633         bd.setHeight(height-bd.getMargins("tb"));
32634         var hh = this.header.getHeight();
32635         var h = this.size.height-hh;
32636         cb.setHeight(h);
32637         
32638         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32639         bw.setHeight(h-cb.getPadding("tb"));
32640         
32641         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32642         bd.setWidth(bw.getWidth(true));
32643         if(this.tabs){
32644             this.tabs.syncHeight();
32645             if(Roo.isIE){
32646                 this.tabs.el.repaint();
32647             }
32648         }
32649     },
32650
32651     /**
32652      * Restores the previous state of the dialog if Roo.state is configured.
32653      * @return {Roo.BasicDialog} this
32654      */
32655     restoreState : function(){
32656         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32657         if(box && box.width){
32658             this.xy = [box.x, box.y];
32659             this.resizeTo(box.width, box.height);
32660         }
32661         return this;
32662     },
32663
32664     // private
32665     beforeShow : function(){
32666         this.expand();
32667         if(this.fixedcenter){
32668             this.xy = this.el.getCenterXY(true);
32669         }
32670         if(this.modal){
32671             Roo.get(document.body).addClass("x-body-masked");
32672             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32673             this.mask.show();
32674         }
32675         this.constrainXY();
32676     },
32677
32678     // private
32679     animShow : function(){
32680         var b = Roo.get(this.animateTarget).getBox();
32681         this.proxy.setSize(b.width, b.height);
32682         this.proxy.setLocation(b.x, b.y);
32683         this.proxy.show();
32684         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32685                     true, .35, this.showEl.createDelegate(this));
32686     },
32687
32688     /**
32689      * Shows the dialog.
32690      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32691      * @return {Roo.BasicDialog} this
32692      */
32693     show : function(animateTarget){
32694         if (this.fireEvent("beforeshow", this) === false){
32695             return;
32696         }
32697         if(this.syncHeightBeforeShow){
32698             this.syncBodyHeight();
32699         }else if(this.firstShow){
32700             this.firstShow = false;
32701             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32702         }
32703         this.animateTarget = animateTarget || this.animateTarget;
32704         if(!this.el.isVisible()){
32705             this.beforeShow();
32706             if(this.animateTarget && Roo.get(this.animateTarget)){
32707                 this.animShow();
32708             }else{
32709                 this.showEl();
32710             }
32711         }
32712         return this;
32713     },
32714
32715     // private
32716     showEl : function(){
32717         this.proxy.hide();
32718         this.el.setXY(this.xy);
32719         this.el.show();
32720         this.adjustAssets(true);
32721         this.toFront();
32722         this.focus();
32723         // IE peekaboo bug - fix found by Dave Fenwick
32724         if(Roo.isIE){
32725             this.el.repaint();
32726         }
32727         this.fireEvent("show", this);
32728     },
32729
32730     /**
32731      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32732      * dialog itself will receive focus.
32733      */
32734     focus : function(){
32735         if(this.defaultButton){
32736             this.defaultButton.focus();
32737         }else{
32738             this.focusEl.focus();
32739         }
32740     },
32741
32742     // private
32743     constrainXY : function(){
32744         if(this.constraintoviewport !== false){
32745             if(!this.viewSize){
32746                 if(this.container){
32747                     var s = this.container.getSize();
32748                     this.viewSize = [s.width, s.height];
32749                 }else{
32750                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32751                 }
32752             }
32753             var s = Roo.get(this.container||document).getScroll();
32754
32755             var x = this.xy[0], y = this.xy[1];
32756             var w = this.size.width, h = this.size.height;
32757             var vw = this.viewSize[0], vh = this.viewSize[1];
32758             // only move it if it needs it
32759             var moved = false;
32760             // first validate right/bottom
32761             if(x + w > vw+s.left){
32762                 x = vw - w;
32763                 moved = true;
32764             }
32765             if(y + h > vh+s.top){
32766                 y = vh - h;
32767                 moved = true;
32768             }
32769             // then make sure top/left isn't negative
32770             if(x < s.left){
32771                 x = s.left;
32772                 moved = true;
32773             }
32774             if(y < s.top){
32775                 y = s.top;
32776                 moved = true;
32777             }
32778             if(moved){
32779                 // cache xy
32780                 this.xy = [x, y];
32781                 if(this.isVisible()){
32782                     this.el.setLocation(x, y);
32783                     this.adjustAssets();
32784                 }
32785             }
32786         }
32787     },
32788
32789     // private
32790     onDrag : function(){
32791         if(!this.proxyDrag){
32792             this.xy = this.el.getXY();
32793             this.adjustAssets();
32794         }
32795     },
32796
32797     // private
32798     adjustAssets : function(doShow){
32799         var x = this.xy[0], y = this.xy[1];
32800         var w = this.size.width, h = this.size.height;
32801         if(doShow === true){
32802             if(this.shadow){
32803                 this.shadow.show(this.el);
32804             }
32805             if(this.shim){
32806                 this.shim.show();
32807             }
32808         }
32809         if(this.shadow && this.shadow.isVisible()){
32810             this.shadow.show(this.el);
32811         }
32812         if(this.shim && this.shim.isVisible()){
32813             this.shim.setBounds(x, y, w, h);
32814         }
32815     },
32816
32817     // private
32818     adjustViewport : function(w, h){
32819         if(!w || !h){
32820             w = Roo.lib.Dom.getViewWidth();
32821             h = Roo.lib.Dom.getViewHeight();
32822         }
32823         // cache the size
32824         this.viewSize = [w, h];
32825         if(this.modal && this.mask.isVisible()){
32826             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32827             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32828         }
32829         if(this.isVisible()){
32830             this.constrainXY();
32831         }
32832     },
32833
32834     /**
32835      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32836      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32837      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32838      */
32839     destroy : function(removeEl){
32840         if(this.isVisible()){
32841             this.animateTarget = null;
32842             this.hide();
32843         }
32844         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32845         if(this.tabs){
32846             this.tabs.destroy(removeEl);
32847         }
32848         Roo.destroy(
32849              this.shim,
32850              this.proxy,
32851              this.resizer,
32852              this.close,
32853              this.mask
32854         );
32855         if(this.dd){
32856             this.dd.unreg();
32857         }
32858         if(this.buttons){
32859            for(var i = 0, len = this.buttons.length; i < len; i++){
32860                this.buttons[i].destroy();
32861            }
32862         }
32863         this.el.removeAllListeners();
32864         if(removeEl === true){
32865             this.el.update("");
32866             this.el.remove();
32867         }
32868         Roo.DialogManager.unregister(this);
32869     },
32870
32871     // private
32872     startMove : function(){
32873         if(this.proxyDrag){
32874             this.proxy.show();
32875         }
32876         if(this.constraintoviewport !== false){
32877             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32878         }
32879     },
32880
32881     // private
32882     endMove : function(){
32883         if(!this.proxyDrag){
32884             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32885         }else{
32886             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32887             this.proxy.hide();
32888         }
32889         this.refreshSize();
32890         this.adjustAssets();
32891         this.focus();
32892         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32893     },
32894
32895     /**
32896      * Brings this dialog to the front of any other visible dialogs
32897      * @return {Roo.BasicDialog} this
32898      */
32899     toFront : function(){
32900         Roo.DialogManager.bringToFront(this);
32901         return this;
32902     },
32903
32904     /**
32905      * Sends this dialog to the back (under) of any other visible dialogs
32906      * @return {Roo.BasicDialog} this
32907      */
32908     toBack : function(){
32909         Roo.DialogManager.sendToBack(this);
32910         return this;
32911     },
32912
32913     /**
32914      * Centers this dialog in the viewport
32915      * @return {Roo.BasicDialog} this
32916      */
32917     center : function(){
32918         var xy = this.el.getCenterXY(true);
32919         this.moveTo(xy[0], xy[1]);
32920         return this;
32921     },
32922
32923     /**
32924      * Moves the dialog's top-left corner to the specified point
32925      * @param {Number} x
32926      * @param {Number} y
32927      * @return {Roo.BasicDialog} this
32928      */
32929     moveTo : function(x, y){
32930         this.xy = [x,y];
32931         if(this.isVisible()){
32932             this.el.setXY(this.xy);
32933             this.adjustAssets();
32934         }
32935         return this;
32936     },
32937
32938     /**
32939      * Aligns the dialog to the specified element
32940      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32941      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
32942      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32943      * @return {Roo.BasicDialog} this
32944      */
32945     alignTo : function(element, position, offsets){
32946         this.xy = this.el.getAlignToXY(element, position, offsets);
32947         if(this.isVisible()){
32948             this.el.setXY(this.xy);
32949             this.adjustAssets();
32950         }
32951         return this;
32952     },
32953
32954     /**
32955      * Anchors an element to another element and realigns it when the window is resized.
32956      * @param {String/HTMLElement/Roo.Element} element The element to align to.
32957      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
32958      * @param {Array} offsets (optional) Offset the positioning by [x, y]
32959      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
32960      * is a number, it is used as the buffer delay (defaults to 50ms).
32961      * @return {Roo.BasicDialog} this
32962      */
32963     anchorTo : function(el, alignment, offsets, monitorScroll){
32964         var action = function(){
32965             this.alignTo(el, alignment, offsets);
32966         };
32967         Roo.EventManager.onWindowResize(action, this);
32968         var tm = typeof monitorScroll;
32969         if(tm != 'undefined'){
32970             Roo.EventManager.on(window, 'scroll', action, this,
32971                 {buffer: tm == 'number' ? monitorScroll : 50});
32972         }
32973         action.call(this);
32974         return this;
32975     },
32976
32977     /**
32978      * Returns true if the dialog is visible
32979      * @return {Boolean}
32980      */
32981     isVisible : function(){
32982         return this.el.isVisible();
32983     },
32984
32985     // private
32986     animHide : function(callback){
32987         var b = Roo.get(this.animateTarget).getBox();
32988         this.proxy.show();
32989         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
32990         this.el.hide();
32991         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
32992                     this.hideEl.createDelegate(this, [callback]));
32993     },
32994
32995     /**
32996      * Hides the dialog.
32997      * @param {Function} callback (optional) Function to call when the dialog is hidden
32998      * @return {Roo.BasicDialog} this
32999      */
33000     hide : function(callback){
33001         if (this.fireEvent("beforehide", this) === false){
33002             return;
33003         }
33004         if(this.shadow){
33005             this.shadow.hide();
33006         }
33007         if(this.shim) {
33008           this.shim.hide();
33009         }
33010         // sometimes animateTarget seems to get set.. causing problems...
33011         // this just double checks..
33012         if(this.animateTarget && Roo.get(this.animateTarget)) {
33013            this.animHide(callback);
33014         }else{
33015             this.el.hide();
33016             this.hideEl(callback);
33017         }
33018         return this;
33019     },
33020
33021     // private
33022     hideEl : function(callback){
33023         this.proxy.hide();
33024         if(this.modal){
33025             this.mask.hide();
33026             Roo.get(document.body).removeClass("x-body-masked");
33027         }
33028         this.fireEvent("hide", this);
33029         if(typeof callback == "function"){
33030             callback();
33031         }
33032     },
33033
33034     // private
33035     hideAction : function(){
33036         this.setLeft("-10000px");
33037         this.setTop("-10000px");
33038         this.setStyle("visibility", "hidden");
33039     },
33040
33041     // private
33042     refreshSize : function(){
33043         this.size = this.el.getSize();
33044         this.xy = this.el.getXY();
33045         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33046     },
33047
33048     // private
33049     // z-index is managed by the DialogManager and may be overwritten at any time
33050     setZIndex : function(index){
33051         if(this.modal){
33052             this.mask.setStyle("z-index", index);
33053         }
33054         if(this.shim){
33055             this.shim.setStyle("z-index", ++index);
33056         }
33057         if(this.shadow){
33058             this.shadow.setZIndex(++index);
33059         }
33060         this.el.setStyle("z-index", ++index);
33061         if(this.proxy){
33062             this.proxy.setStyle("z-index", ++index);
33063         }
33064         if(this.resizer){
33065             this.resizer.proxy.setStyle("z-index", ++index);
33066         }
33067
33068         this.lastZIndex = index;
33069     },
33070
33071     /**
33072      * Returns the element for this dialog
33073      * @return {Roo.Element} The underlying dialog Element
33074      */
33075     getEl : function(){
33076         return this.el;
33077     }
33078 });
33079
33080 /**
33081  * @class Roo.DialogManager
33082  * Provides global access to BasicDialogs that have been created and
33083  * support for z-indexing (layering) multiple open dialogs.
33084  */
33085 Roo.DialogManager = function(){
33086     var list = {};
33087     var accessList = [];
33088     var front = null;
33089
33090     // private
33091     var sortDialogs = function(d1, d2){
33092         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33093     };
33094
33095     // private
33096     var orderDialogs = function(){
33097         accessList.sort(sortDialogs);
33098         var seed = Roo.DialogManager.zseed;
33099         for(var i = 0, len = accessList.length; i < len; i++){
33100             var dlg = accessList[i];
33101             if(dlg){
33102                 dlg.setZIndex(seed + (i*10));
33103             }
33104         }
33105     };
33106
33107     return {
33108         /**
33109          * The starting z-index for BasicDialogs (defaults to 9000)
33110          * @type Number The z-index value
33111          */
33112         zseed : 9000,
33113
33114         // private
33115         register : function(dlg){
33116             list[dlg.id] = dlg;
33117             accessList.push(dlg);
33118         },
33119
33120         // private
33121         unregister : function(dlg){
33122             delete list[dlg.id];
33123             var i=0;
33124             var len=0;
33125             if(!accessList.indexOf){
33126                 for(  i = 0, len = accessList.length; i < len; i++){
33127                     if(accessList[i] == dlg){
33128                         accessList.splice(i, 1);
33129                         return;
33130                     }
33131                 }
33132             }else{
33133                  i = accessList.indexOf(dlg);
33134                 if(i != -1){
33135                     accessList.splice(i, 1);
33136                 }
33137             }
33138         },
33139
33140         /**
33141          * Gets a registered dialog by id
33142          * @param {String/Object} id The id of the dialog or a dialog
33143          * @return {Roo.BasicDialog} this
33144          */
33145         get : function(id){
33146             return typeof id == "object" ? id : list[id];
33147         },
33148
33149         /**
33150          * Brings the specified dialog to the front
33151          * @param {String/Object} dlg The id of the dialog or a dialog
33152          * @return {Roo.BasicDialog} this
33153          */
33154         bringToFront : function(dlg){
33155             dlg = this.get(dlg);
33156             if(dlg != front){
33157                 front = dlg;
33158                 dlg._lastAccess = new Date().getTime();
33159                 orderDialogs();
33160             }
33161             return dlg;
33162         },
33163
33164         /**
33165          * Sends the specified dialog to the back
33166          * @param {String/Object} dlg The id of the dialog or a dialog
33167          * @return {Roo.BasicDialog} this
33168          */
33169         sendToBack : function(dlg){
33170             dlg = this.get(dlg);
33171             dlg._lastAccess = -(new Date().getTime());
33172             orderDialogs();
33173             return dlg;
33174         },
33175
33176         /**
33177          * Hides all dialogs
33178          */
33179         hideAll : function(){
33180             for(var id in list){
33181                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33182                     list[id].hide();
33183                 }
33184             }
33185         }
33186     };
33187 }();
33188
33189 /**
33190  * @class Roo.LayoutDialog
33191  * @extends Roo.BasicDialog
33192  * Dialog which provides adjustments for working with a layout in a Dialog.
33193  * Add your necessary layout config options to the dialog's config.<br>
33194  * Example usage (including a nested layout):
33195  * <pre><code>
33196 if(!dialog){
33197     dialog = new Roo.LayoutDialog("download-dlg", {
33198         modal: true,
33199         width:600,
33200         height:450,
33201         shadow:true,
33202         minWidth:500,
33203         minHeight:350,
33204         autoTabs:true,
33205         proxyDrag:true,
33206         // layout config merges with the dialog config
33207         center:{
33208             tabPosition: "top",
33209             alwaysShowTabs: true
33210         }
33211     });
33212     dialog.addKeyListener(27, dialog.hide, dialog);
33213     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33214     dialog.addButton("Build It!", this.getDownload, this);
33215
33216     // we can even add nested layouts
33217     var innerLayout = new Roo.BorderLayout("dl-inner", {
33218         east: {
33219             initialSize: 200,
33220             autoScroll:true,
33221             split:true
33222         },
33223         center: {
33224             autoScroll:true
33225         }
33226     });
33227     innerLayout.beginUpdate();
33228     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33229     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33230     innerLayout.endUpdate(true);
33231
33232     var layout = dialog.getLayout();
33233     layout.beginUpdate();
33234     layout.add("center", new Roo.ContentPanel("standard-panel",
33235                         {title: "Download the Source", fitToFrame:true}));
33236     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33237                {title: "Build your own roo.js"}));
33238     layout.getRegion("center").showPanel(sp);
33239     layout.endUpdate();
33240 }
33241 </code></pre>
33242     * @constructor
33243     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33244     * @param {Object} config configuration options
33245   */
33246 Roo.LayoutDialog = function(el, cfg){
33247     
33248     var config=  cfg;
33249     if (typeof(cfg) == 'undefined') {
33250         config = Roo.apply({}, el);
33251         // not sure why we use documentElement here.. - it should always be body.
33252         // IE7 borks horribly if we use documentElement.
33253         // webkit also does not like documentElement - it creates a body element...
33254         el = Roo.get( document.body || document.documentElement ).createChild();
33255         //config.autoCreate = true;
33256     }
33257     
33258     
33259     config.autoTabs = false;
33260     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33261     this.body.setStyle({overflow:"hidden", position:"relative"});
33262     this.layout = new Roo.BorderLayout(this.body.dom, config);
33263     this.layout.monitorWindowResize = false;
33264     this.el.addClass("x-dlg-auto-layout");
33265     // fix case when center region overwrites center function
33266     this.center = Roo.BasicDialog.prototype.center;
33267     this.on("show", this.layout.layout, this.layout, true);
33268     if (config.items) {
33269         var xitems = config.items;
33270         delete config.items;
33271         Roo.each(xitems, this.addxtype, this);
33272     }
33273     
33274     
33275 };
33276 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33277     /**
33278      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33279      * @deprecated
33280      */
33281     endUpdate : function(){
33282         this.layout.endUpdate();
33283     },
33284
33285     /**
33286      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33287      *  @deprecated
33288      */
33289     beginUpdate : function(){
33290         this.layout.beginUpdate();
33291     },
33292
33293     /**
33294      * Get the BorderLayout for this dialog
33295      * @return {Roo.BorderLayout}
33296      */
33297     getLayout : function(){
33298         return this.layout;
33299     },
33300
33301     showEl : function(){
33302         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33303         if(Roo.isIE7){
33304             this.layout.layout();
33305         }
33306     },
33307
33308     // private
33309     // Use the syncHeightBeforeShow config option to control this automatically
33310     syncBodyHeight : function(){
33311         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33312         if(this.layout){this.layout.layout();}
33313     },
33314     
33315       /**
33316      * Add an xtype element (actually adds to the layout.)
33317      * @return {Object} xdata xtype object data.
33318      */
33319     
33320     addxtype : function(c) {
33321         return this.layout.addxtype(c);
33322     }
33323 });/*
33324  * Based on:
33325  * Ext JS Library 1.1.1
33326  * Copyright(c) 2006-2007, Ext JS, LLC.
33327  *
33328  * Originally Released Under LGPL - original licence link has changed is not relivant.
33329  *
33330  * Fork - LGPL
33331  * <script type="text/javascript">
33332  */
33333  
33334 /**
33335  * @class Roo.MessageBox
33336  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33337  * Example usage:
33338  *<pre><code>
33339 // Basic alert:
33340 Roo.Msg.alert('Status', 'Changes saved successfully.');
33341
33342 // Prompt for user data:
33343 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33344     if (btn == 'ok'){
33345         // process text value...
33346     }
33347 });
33348
33349 // Show a dialog using config options:
33350 Roo.Msg.show({
33351    title:'Save Changes?',
33352    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33353    buttons: Roo.Msg.YESNOCANCEL,
33354    fn: processResult,
33355    animEl: 'elId'
33356 });
33357 </code></pre>
33358  * @singleton
33359  */
33360 Roo.MessageBox = function(){
33361     var dlg, opt, mask, waitTimer;
33362     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33363     var buttons, activeTextEl, bwidth;
33364
33365     // private
33366     var handleButton = function(button){
33367         dlg.hide();
33368         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33369     };
33370
33371     // private
33372     var handleHide = function(){
33373         if(opt && opt.cls){
33374             dlg.el.removeClass(opt.cls);
33375         }
33376         if(waitTimer){
33377             Roo.TaskMgr.stop(waitTimer);
33378             waitTimer = null;
33379         }
33380     };
33381
33382     // private
33383     var updateButtons = function(b){
33384         var width = 0;
33385         if(!b){
33386             buttons["ok"].hide();
33387             buttons["cancel"].hide();
33388             buttons["yes"].hide();
33389             buttons["no"].hide();
33390             dlg.footer.dom.style.display = 'none';
33391             return width;
33392         }
33393         dlg.footer.dom.style.display = '';
33394         for(var k in buttons){
33395             if(typeof buttons[k] != "function"){
33396                 if(b[k]){
33397                     buttons[k].show();
33398                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33399                     width += buttons[k].el.getWidth()+15;
33400                 }else{
33401                     buttons[k].hide();
33402                 }
33403             }
33404         }
33405         return width;
33406     };
33407
33408     // private
33409     var handleEsc = function(d, k, e){
33410         if(opt && opt.closable !== false){
33411             dlg.hide();
33412         }
33413         if(e){
33414             e.stopEvent();
33415         }
33416     };
33417
33418     return {
33419         /**
33420          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33421          * @return {Roo.BasicDialog} The BasicDialog element
33422          */
33423         getDialog : function(){
33424            if(!dlg){
33425                 dlg = new Roo.BasicDialog("x-msg-box", {
33426                     autoCreate : true,
33427                     shadow: true,
33428                     draggable: true,
33429                     resizable:false,
33430                     constraintoviewport:false,
33431                     fixedcenter:true,
33432                     collapsible : false,
33433                     shim:true,
33434                     modal: true,
33435                     width:400, height:100,
33436                     buttonAlign:"center",
33437                     closeClick : function(){
33438                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33439                             handleButton("no");
33440                         }else{
33441                             handleButton("cancel");
33442                         }
33443                     }
33444                 });
33445                 dlg.on("hide", handleHide);
33446                 mask = dlg.mask;
33447                 dlg.addKeyListener(27, handleEsc);
33448                 buttons = {};
33449                 var bt = this.buttonText;
33450                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33451                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33452                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33453                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33454                 bodyEl = dlg.body.createChild({
33455
33456                     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>'
33457                 });
33458                 msgEl = bodyEl.dom.firstChild;
33459                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33460                 textboxEl.enableDisplayMode();
33461                 textboxEl.addKeyListener([10,13], function(){
33462                     if(dlg.isVisible() && opt && opt.buttons){
33463                         if(opt.buttons.ok){
33464                             handleButton("ok");
33465                         }else if(opt.buttons.yes){
33466                             handleButton("yes");
33467                         }
33468                     }
33469                 });
33470                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33471                 textareaEl.enableDisplayMode();
33472                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33473                 progressEl.enableDisplayMode();
33474                 var pf = progressEl.dom.firstChild;
33475                 if (pf) {
33476                     pp = Roo.get(pf.firstChild);
33477                     pp.setHeight(pf.offsetHeight);
33478                 }
33479                 
33480             }
33481             return dlg;
33482         },
33483
33484         /**
33485          * Updates the message box body text
33486          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33487          * the XHTML-compliant non-breaking space character '&amp;#160;')
33488          * @return {Roo.MessageBox} This message box
33489          */
33490         updateText : function(text){
33491             if(!dlg.isVisible() && !opt.width){
33492                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33493             }
33494             msgEl.innerHTML = text || '&#160;';
33495       
33496             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33497             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33498             var w = Math.max(
33499                     Math.min(opt.width || cw , this.maxWidth), 
33500                     Math.max(opt.minWidth || this.minWidth, bwidth)
33501             );
33502             if(opt.prompt){
33503                 activeTextEl.setWidth(w);
33504             }
33505             if(dlg.isVisible()){
33506                 dlg.fixedcenter = false;
33507             }
33508             // to big, make it scroll. = But as usual stupid IE does not support
33509             // !important..
33510             
33511             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33512                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33513                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33514             } else {
33515                 bodyEl.dom.style.height = '';
33516                 bodyEl.dom.style.overflowY = '';
33517             }
33518             if (cw > w) {
33519                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33520             } else {
33521                 bodyEl.dom.style.overflowX = '';
33522             }
33523             
33524             dlg.setContentSize(w, bodyEl.getHeight());
33525             if(dlg.isVisible()){
33526                 dlg.fixedcenter = true;
33527             }
33528             return this;
33529         },
33530
33531         /**
33532          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33533          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33534          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33535          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33536          * @return {Roo.MessageBox} This message box
33537          */
33538         updateProgress : function(value, text){
33539             if(text){
33540                 this.updateText(text);
33541             }
33542             if (pp) { // weird bug on my firefox - for some reason this is not defined
33543                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33544             }
33545             return this;
33546         },        
33547
33548         /**
33549          * Returns true if the message box is currently displayed
33550          * @return {Boolean} True if the message box is visible, else false
33551          */
33552         isVisible : function(){
33553             return dlg && dlg.isVisible();  
33554         },
33555
33556         /**
33557          * Hides the message box if it is displayed
33558          */
33559         hide : function(){
33560             if(this.isVisible()){
33561                 dlg.hide();
33562             }  
33563         },
33564
33565         /**
33566          * Displays a new message box, or reinitializes an existing message box, based on the config options
33567          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33568          * The following config object properties are supported:
33569          * <pre>
33570 Property    Type             Description
33571 ----------  ---------------  ------------------------------------------------------------------------------------
33572 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33573                                    closes (defaults to undefined)
33574 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33575                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33576 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33577                                    progress and wait dialogs will ignore this property and always hide the
33578                                    close button as they can only be closed programmatically.
33579 cls               String           A custom CSS class to apply to the message box element
33580 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33581                                    displayed (defaults to 75)
33582 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33583                                    function will be btn (the name of the button that was clicked, if applicable,
33584                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33585                                    Progress and wait dialogs will ignore this option since they do not respond to
33586                                    user actions and can only be closed programmatically, so any required function
33587                                    should be called by the same code after it closes the dialog.
33588 icon              String           A CSS class that provides a background image to be used as an icon for
33589                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33590 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33591 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33592 modal             Boolean          False to allow user interaction with the page while the message box is
33593                                    displayed (defaults to true)
33594 msg               String           A string that will replace the existing message box body text (defaults
33595                                    to the XHTML-compliant non-breaking space character '&#160;')
33596 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33597 progress          Boolean          True to display a progress bar (defaults to false)
33598 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33599 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33600 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33601 title             String           The title text
33602 value             String           The string value to set into the active textbox element if displayed
33603 wait              Boolean          True to display a progress bar (defaults to false)
33604 width             Number           The width of the dialog in pixels
33605 </pre>
33606          *
33607          * Example usage:
33608          * <pre><code>
33609 Roo.Msg.show({
33610    title: 'Address',
33611    msg: 'Please enter your address:',
33612    width: 300,
33613    buttons: Roo.MessageBox.OKCANCEL,
33614    multiline: true,
33615    fn: saveAddress,
33616    animEl: 'addAddressBtn'
33617 });
33618 </code></pre>
33619          * @param {Object} config Configuration options
33620          * @return {Roo.MessageBox} This message box
33621          */
33622         show : function(options)
33623         {
33624             
33625             // this causes nightmares if you show one dialog after another
33626             // especially on callbacks..
33627              
33628             if(this.isVisible()){
33629                 
33630                 this.hide();
33631                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33632                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33633                 Roo.log("New Dialog Message:" +  options.msg )
33634                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33635                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33636                 
33637             }
33638             var d = this.getDialog();
33639             opt = options;
33640             d.setTitle(opt.title || "&#160;");
33641             d.close.setDisplayed(opt.closable !== false);
33642             activeTextEl = textboxEl;
33643             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33644             if(opt.prompt){
33645                 if(opt.multiline){
33646                     textboxEl.hide();
33647                     textareaEl.show();
33648                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33649                         opt.multiline : this.defaultTextHeight);
33650                     activeTextEl = textareaEl;
33651                 }else{
33652                     textboxEl.show();
33653                     textareaEl.hide();
33654                 }
33655             }else{
33656                 textboxEl.hide();
33657                 textareaEl.hide();
33658             }
33659             progressEl.setDisplayed(opt.progress === true);
33660             this.updateProgress(0);
33661             activeTextEl.dom.value = opt.value || "";
33662             if(opt.prompt){
33663                 dlg.setDefaultButton(activeTextEl);
33664             }else{
33665                 var bs = opt.buttons;
33666                 var db = null;
33667                 if(bs && bs.ok){
33668                     db = buttons["ok"];
33669                 }else if(bs && bs.yes){
33670                     db = buttons["yes"];
33671                 }
33672                 dlg.setDefaultButton(db);
33673             }
33674             bwidth = updateButtons(opt.buttons);
33675             this.updateText(opt.msg);
33676             if(opt.cls){
33677                 d.el.addClass(opt.cls);
33678             }
33679             d.proxyDrag = opt.proxyDrag === true;
33680             d.modal = opt.modal !== false;
33681             d.mask = opt.modal !== false ? mask : false;
33682             if(!d.isVisible()){
33683                 // force it to the end of the z-index stack so it gets a cursor in FF
33684                 document.body.appendChild(dlg.el.dom);
33685                 d.animateTarget = null;
33686                 d.show(options.animEl);
33687             }
33688             return this;
33689         },
33690
33691         /**
33692          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33693          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33694          * and closing the message box when the process is complete.
33695          * @param {String} title The title bar text
33696          * @param {String} msg The message box body text
33697          * @return {Roo.MessageBox} This message box
33698          */
33699         progress : function(title, msg){
33700             this.show({
33701                 title : title,
33702                 msg : msg,
33703                 buttons: false,
33704                 progress:true,
33705                 closable:false,
33706                 minWidth: this.minProgressWidth,
33707                 modal : true
33708             });
33709             return this;
33710         },
33711
33712         /**
33713          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33714          * If a callback function is passed it will be called after the user clicks the button, and the
33715          * id of the button that was clicked will be passed as the only parameter to the callback
33716          * (could also be the top-right close button).
33717          * @param {String} title The title bar text
33718          * @param {String} msg The message box body text
33719          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33720          * @param {Object} scope (optional) The scope of the callback function
33721          * @return {Roo.MessageBox} This message box
33722          */
33723         alert : function(title, msg, fn, scope){
33724             this.show({
33725                 title : title,
33726                 msg : msg,
33727                 buttons: this.OK,
33728                 fn: fn,
33729                 scope : scope,
33730                 modal : true
33731             });
33732             return this;
33733         },
33734
33735         /**
33736          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33737          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33738          * You are responsible for closing the message box when the process is complete.
33739          * @param {String} msg The message box body text
33740          * @param {String} title (optional) The title bar text
33741          * @return {Roo.MessageBox} This message box
33742          */
33743         wait : function(msg, title){
33744             this.show({
33745                 title : title,
33746                 msg : msg,
33747                 buttons: false,
33748                 closable:false,
33749                 progress:true,
33750                 modal:true,
33751                 width:300,
33752                 wait:true
33753             });
33754             waitTimer = Roo.TaskMgr.start({
33755                 run: function(i){
33756                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33757                 },
33758                 interval: 1000
33759             });
33760             return this;
33761         },
33762
33763         /**
33764          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33765          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33766          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33767          * @param {String} title The title bar text
33768          * @param {String} msg The message box body text
33769          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33770          * @param {Object} scope (optional) The scope of the callback function
33771          * @return {Roo.MessageBox} This message box
33772          */
33773         confirm : function(title, msg, fn, scope){
33774             this.show({
33775                 title : title,
33776                 msg : msg,
33777                 buttons: this.YESNO,
33778                 fn: fn,
33779                 scope : scope,
33780                 modal : true
33781             });
33782             return this;
33783         },
33784
33785         /**
33786          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33787          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33788          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33789          * (could also be the top-right close button) and the text that was entered will be passed as the two
33790          * parameters to the callback.
33791          * @param {String} title The title bar text
33792          * @param {String} msg The message box body text
33793          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33794          * @param {Object} scope (optional) The scope of the callback function
33795          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33796          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33797          * @return {Roo.MessageBox} This message box
33798          */
33799         prompt : function(title, msg, fn, scope, multiline){
33800             this.show({
33801                 title : title,
33802                 msg : msg,
33803                 buttons: this.OKCANCEL,
33804                 fn: fn,
33805                 minWidth:250,
33806                 scope : scope,
33807                 prompt:true,
33808                 multiline: multiline,
33809                 modal : true
33810             });
33811             return this;
33812         },
33813
33814         /**
33815          * Button config that displays a single OK button
33816          * @type Object
33817          */
33818         OK : {ok:true},
33819         /**
33820          * Button config that displays Yes and No buttons
33821          * @type Object
33822          */
33823         YESNO : {yes:true, no:true},
33824         /**
33825          * Button config that displays OK and Cancel buttons
33826          * @type Object
33827          */
33828         OKCANCEL : {ok:true, cancel:true},
33829         /**
33830          * Button config that displays Yes, No and Cancel buttons
33831          * @type Object
33832          */
33833         YESNOCANCEL : {yes:true, no:true, cancel:true},
33834
33835         /**
33836          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33837          * @type Number
33838          */
33839         defaultTextHeight : 75,
33840         /**
33841          * The maximum width in pixels of the message box (defaults to 600)
33842          * @type Number
33843          */
33844         maxWidth : 600,
33845         /**
33846          * The minimum width in pixels of the message box (defaults to 100)
33847          * @type Number
33848          */
33849         minWidth : 100,
33850         /**
33851          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33852          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33853          * @type Number
33854          */
33855         minProgressWidth : 250,
33856         /**
33857          * An object containing the default button text strings that can be overriden for localized language support.
33858          * Supported properties are: ok, cancel, yes and no.
33859          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33860          * @type Object
33861          */
33862         buttonText : {
33863             ok : "OK",
33864             cancel : "Cancel",
33865             yes : "Yes",
33866             no : "No"
33867         }
33868     };
33869 }();
33870
33871 /**
33872  * Shorthand for {@link Roo.MessageBox}
33873  */
33874 Roo.Msg = Roo.MessageBox;/*
33875  * Based on:
33876  * Ext JS Library 1.1.1
33877  * Copyright(c) 2006-2007, Ext JS, LLC.
33878  *
33879  * Originally Released Under LGPL - original licence link has changed is not relivant.
33880  *
33881  * Fork - LGPL
33882  * <script type="text/javascript">
33883  */
33884 /**
33885  * @class Roo.QuickTips
33886  * Provides attractive and customizable tooltips for any element.
33887  * @singleton
33888  */
33889 Roo.QuickTips = function(){
33890     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33891     var ce, bd, xy, dd;
33892     var visible = false, disabled = true, inited = false;
33893     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33894     
33895     var onOver = function(e){
33896         if(disabled){
33897             return;
33898         }
33899         var t = e.getTarget();
33900         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33901             return;
33902         }
33903         if(ce && t == ce.el){
33904             clearTimeout(hideProc);
33905             return;
33906         }
33907         if(t && tagEls[t.id]){
33908             tagEls[t.id].el = t;
33909             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33910             return;
33911         }
33912         var ttp, et = Roo.fly(t);
33913         var ns = cfg.namespace;
33914         if(tm.interceptTitles && t.title){
33915             ttp = t.title;
33916             t.qtip = ttp;
33917             t.removeAttribute("title");
33918             e.preventDefault();
33919         }else{
33920             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33921         }
33922         if(ttp){
33923             showProc = show.defer(tm.showDelay, tm, [{
33924                 el: t, 
33925                 text: ttp.replace(/\\n/g,'<br/>'),
33926                 width: et.getAttributeNS(ns, cfg.width),
33927                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33928                 title: et.getAttributeNS(ns, cfg.title),
33929                     cls: et.getAttributeNS(ns, cfg.cls)
33930             }]);
33931         }
33932     };
33933     
33934     var onOut = function(e){
33935         clearTimeout(showProc);
33936         var t = e.getTarget();
33937         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33938             hideProc = setTimeout(hide, tm.hideDelay);
33939         }
33940     };
33941     
33942     var onMove = function(e){
33943         if(disabled){
33944             return;
33945         }
33946         xy = e.getXY();
33947         xy[1] += 18;
33948         if(tm.trackMouse && ce){
33949             el.setXY(xy);
33950         }
33951     };
33952     
33953     var onDown = function(e){
33954         clearTimeout(showProc);
33955         clearTimeout(hideProc);
33956         if(!e.within(el)){
33957             if(tm.hideOnClick){
33958                 hide();
33959                 tm.disable();
33960                 tm.enable.defer(100, tm);
33961             }
33962         }
33963     };
33964     
33965     var getPad = function(){
33966         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
33967     };
33968
33969     var show = function(o){
33970         if(disabled){
33971             return;
33972         }
33973         clearTimeout(dismissProc);
33974         ce = o;
33975         if(removeCls){ // in case manually hidden
33976             el.removeClass(removeCls);
33977             removeCls = null;
33978         }
33979         if(ce.cls){
33980             el.addClass(ce.cls);
33981             removeCls = ce.cls;
33982         }
33983         if(ce.title){
33984             tipTitle.update(ce.title);
33985             tipTitle.show();
33986         }else{
33987             tipTitle.update('');
33988             tipTitle.hide();
33989         }
33990         el.dom.style.width  = tm.maxWidth+'px';
33991         //tipBody.dom.style.width = '';
33992         tipBodyText.update(o.text);
33993         var p = getPad(), w = ce.width;
33994         if(!w){
33995             var td = tipBodyText.dom;
33996             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
33997             if(aw > tm.maxWidth){
33998                 w = tm.maxWidth;
33999             }else if(aw < tm.minWidth){
34000                 w = tm.minWidth;
34001             }else{
34002                 w = aw;
34003             }
34004         }
34005         //tipBody.setWidth(w);
34006         el.setWidth(parseInt(w, 10) + p);
34007         if(ce.autoHide === false){
34008             close.setDisplayed(true);
34009             if(dd){
34010                 dd.unlock();
34011             }
34012         }else{
34013             close.setDisplayed(false);
34014             if(dd){
34015                 dd.lock();
34016             }
34017         }
34018         if(xy){
34019             el.avoidY = xy[1]-18;
34020             el.setXY(xy);
34021         }
34022         if(tm.animate){
34023             el.setOpacity(.1);
34024             el.setStyle("visibility", "visible");
34025             el.fadeIn({callback: afterShow});
34026         }else{
34027             afterShow();
34028         }
34029     };
34030     
34031     var afterShow = function(){
34032         if(ce){
34033             el.show();
34034             esc.enable();
34035             if(tm.autoDismiss && ce.autoHide !== false){
34036                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34037             }
34038         }
34039     };
34040     
34041     var hide = function(noanim){
34042         clearTimeout(dismissProc);
34043         clearTimeout(hideProc);
34044         ce = null;
34045         if(el.isVisible()){
34046             esc.disable();
34047             if(noanim !== true && tm.animate){
34048                 el.fadeOut({callback: afterHide});
34049             }else{
34050                 afterHide();
34051             } 
34052         }
34053     };
34054     
34055     var afterHide = function(){
34056         el.hide();
34057         if(removeCls){
34058             el.removeClass(removeCls);
34059             removeCls = null;
34060         }
34061     };
34062     
34063     return {
34064         /**
34065         * @cfg {Number} minWidth
34066         * The minimum width of the quick tip (defaults to 40)
34067         */
34068        minWidth : 40,
34069         /**
34070         * @cfg {Number} maxWidth
34071         * The maximum width of the quick tip (defaults to 300)
34072         */
34073        maxWidth : 300,
34074         /**
34075         * @cfg {Boolean} interceptTitles
34076         * True to automatically use the element's DOM title value if available (defaults to false)
34077         */
34078        interceptTitles : false,
34079         /**
34080         * @cfg {Boolean} trackMouse
34081         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34082         */
34083        trackMouse : false,
34084         /**
34085         * @cfg {Boolean} hideOnClick
34086         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34087         */
34088        hideOnClick : true,
34089         /**
34090         * @cfg {Number} showDelay
34091         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34092         */
34093        showDelay : 500,
34094         /**
34095         * @cfg {Number} hideDelay
34096         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34097         */
34098        hideDelay : 200,
34099         /**
34100         * @cfg {Boolean} autoHide
34101         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34102         * Used in conjunction with hideDelay.
34103         */
34104        autoHide : true,
34105         /**
34106         * @cfg {Boolean}
34107         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34108         * (defaults to true).  Used in conjunction with autoDismissDelay.
34109         */
34110        autoDismiss : true,
34111         /**
34112         * @cfg {Number}
34113         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34114         */
34115        autoDismissDelay : 5000,
34116        /**
34117         * @cfg {Boolean} animate
34118         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34119         */
34120        animate : false,
34121
34122        /**
34123         * @cfg {String} title
34124         * Title text to display (defaults to '').  This can be any valid HTML markup.
34125         */
34126         title: '',
34127        /**
34128         * @cfg {String} text
34129         * Body text to display (defaults to '').  This can be any valid HTML markup.
34130         */
34131         text : '',
34132        /**
34133         * @cfg {String} cls
34134         * A CSS class to apply to the base quick tip element (defaults to '').
34135         */
34136         cls : '',
34137        /**
34138         * @cfg {Number} width
34139         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34140         * minWidth or maxWidth.
34141         */
34142         width : null,
34143
34144     /**
34145      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34146      * or display QuickTips in a page.
34147      */
34148        init : function(){
34149           tm = Roo.QuickTips;
34150           cfg = tm.tagConfig;
34151           if(!inited){
34152               if(!Roo.isReady){ // allow calling of init() before onReady
34153                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34154                   return;
34155               }
34156               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34157               el.fxDefaults = {stopFx: true};
34158               // maximum custom styling
34159               //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>');
34160               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>');              
34161               tipTitle = el.child('h3');
34162               tipTitle.enableDisplayMode("block");
34163               tipBody = el.child('div.x-tip-bd');
34164               tipBodyText = el.child('div.x-tip-bd-inner');
34165               //bdLeft = el.child('div.x-tip-bd-left');
34166               //bdRight = el.child('div.x-tip-bd-right');
34167               close = el.child('div.x-tip-close');
34168               close.enableDisplayMode("block");
34169               close.on("click", hide);
34170               var d = Roo.get(document);
34171               d.on("mousedown", onDown);
34172               d.on("mouseover", onOver);
34173               d.on("mouseout", onOut);
34174               d.on("mousemove", onMove);
34175               esc = d.addKeyListener(27, hide);
34176               esc.disable();
34177               if(Roo.dd.DD){
34178                   dd = el.initDD("default", null, {
34179                       onDrag : function(){
34180                           el.sync();  
34181                       }
34182                   });
34183                   dd.setHandleElId(tipTitle.id);
34184                   dd.lock();
34185               }
34186               inited = true;
34187           }
34188           this.enable(); 
34189        },
34190
34191     /**
34192      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34193      * are supported:
34194      * <pre>
34195 Property    Type                   Description
34196 ----------  ---------------------  ------------------------------------------------------------------------
34197 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34198      * </ul>
34199      * @param {Object} config The config object
34200      */
34201        register : function(config){
34202            var cs = config instanceof Array ? config : arguments;
34203            for(var i = 0, len = cs.length; i < len; i++) {
34204                var c = cs[i];
34205                var target = c.target;
34206                if(target){
34207                    if(target instanceof Array){
34208                        for(var j = 0, jlen = target.length; j < jlen; j++){
34209                            tagEls[target[j]] = c;
34210                        }
34211                    }else{
34212                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34213                    }
34214                }
34215            }
34216        },
34217
34218     /**
34219      * Removes this quick tip from its element and destroys it.
34220      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34221      */
34222        unregister : function(el){
34223            delete tagEls[Roo.id(el)];
34224        },
34225
34226     /**
34227      * Enable this quick tip.
34228      */
34229        enable : function(){
34230            if(inited && disabled){
34231                locks.pop();
34232                if(locks.length < 1){
34233                    disabled = false;
34234                }
34235            }
34236        },
34237
34238     /**
34239      * Disable this quick tip.
34240      */
34241        disable : function(){
34242           disabled = true;
34243           clearTimeout(showProc);
34244           clearTimeout(hideProc);
34245           clearTimeout(dismissProc);
34246           if(ce){
34247               hide(true);
34248           }
34249           locks.push(1);
34250        },
34251
34252     /**
34253      * Returns true if the quick tip is enabled, else false.
34254      */
34255        isEnabled : function(){
34256             return !disabled;
34257        },
34258
34259         // private
34260        tagConfig : {
34261            namespace : "roo", // was ext?? this may break..
34262            alt_namespace : "ext",
34263            attribute : "qtip",
34264            width : "width",
34265            target : "target",
34266            title : "qtitle",
34267            hide : "hide",
34268            cls : "qclass"
34269        }
34270    };
34271 }();
34272
34273 // backwards compat
34274 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34275  * Based on:
34276  * Ext JS Library 1.1.1
34277  * Copyright(c) 2006-2007, Ext JS, LLC.
34278  *
34279  * Originally Released Under LGPL - original licence link has changed is not relivant.
34280  *
34281  * Fork - LGPL
34282  * <script type="text/javascript">
34283  */
34284  
34285
34286 /**
34287  * @class Roo.tree.TreePanel
34288  * @extends Roo.data.Tree
34289
34290  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34291  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34292  * @cfg {Boolean} enableDD true to enable drag and drop
34293  * @cfg {Boolean} enableDrag true to enable just drag
34294  * @cfg {Boolean} enableDrop true to enable just drop
34295  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34296  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34297  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34298  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34299  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34300  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34301  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34302  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34303  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34304  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34305  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34306  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34307  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34308  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34309  * @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>
34310  * @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>
34311  * 
34312  * @constructor
34313  * @param {String/HTMLElement/Element} el The container element
34314  * @param {Object} config
34315  */
34316 Roo.tree.TreePanel = function(el, config){
34317     var root = false;
34318     var loader = false;
34319     if (config.root) {
34320         root = config.root;
34321         delete config.root;
34322     }
34323     if (config.loader) {
34324         loader = config.loader;
34325         delete config.loader;
34326     }
34327     
34328     Roo.apply(this, config);
34329     Roo.tree.TreePanel.superclass.constructor.call(this);
34330     this.el = Roo.get(el);
34331     this.el.addClass('x-tree');
34332     //console.log(root);
34333     if (root) {
34334         this.setRootNode( Roo.factory(root, Roo.tree));
34335     }
34336     if (loader) {
34337         this.loader = Roo.factory(loader, Roo.tree);
34338     }
34339    /**
34340     * Read-only. The id of the container element becomes this TreePanel's id.
34341     */
34342     this.id = this.el.id;
34343     this.addEvents({
34344         /**
34345         * @event beforeload
34346         * Fires before a node is loaded, return false to cancel
34347         * @param {Node} node The node being loaded
34348         */
34349         "beforeload" : true,
34350         /**
34351         * @event load
34352         * Fires when a node is loaded
34353         * @param {Node} node The node that was loaded
34354         */
34355         "load" : true,
34356         /**
34357         * @event textchange
34358         * Fires when the text for a node is changed
34359         * @param {Node} node The node
34360         * @param {String} text The new text
34361         * @param {String} oldText The old text
34362         */
34363         "textchange" : true,
34364         /**
34365         * @event beforeexpand
34366         * Fires before a node is expanded, return false to cancel.
34367         * @param {Node} node The node
34368         * @param {Boolean} deep
34369         * @param {Boolean} anim
34370         */
34371         "beforeexpand" : true,
34372         /**
34373         * @event beforecollapse
34374         * Fires before a node is collapsed, return false to cancel.
34375         * @param {Node} node The node
34376         * @param {Boolean} deep
34377         * @param {Boolean} anim
34378         */
34379         "beforecollapse" : true,
34380         /**
34381         * @event expand
34382         * Fires when a node is expanded
34383         * @param {Node} node The node
34384         */
34385         "expand" : true,
34386         /**
34387         * @event disabledchange
34388         * Fires when the disabled status of a node changes
34389         * @param {Node} node The node
34390         * @param {Boolean} disabled
34391         */
34392         "disabledchange" : true,
34393         /**
34394         * @event collapse
34395         * Fires when a node is collapsed
34396         * @param {Node} node The node
34397         */
34398         "collapse" : true,
34399         /**
34400         * @event beforeclick
34401         * Fires before click processing on a node. Return false to cancel the default action.
34402         * @param {Node} node The node
34403         * @param {Roo.EventObject} e The event object
34404         */
34405         "beforeclick":true,
34406         /**
34407         * @event checkchange
34408         * Fires when a node with a checkbox's checked property changes
34409         * @param {Node} this This node
34410         * @param {Boolean} checked
34411         */
34412         "checkchange":true,
34413         /**
34414         * @event click
34415         * Fires when a node is clicked
34416         * @param {Node} node The node
34417         * @param {Roo.EventObject} e The event object
34418         */
34419         "click":true,
34420         /**
34421         * @event dblclick
34422         * Fires when a node is double clicked
34423         * @param {Node} node The node
34424         * @param {Roo.EventObject} e The event object
34425         */
34426         "dblclick":true,
34427         /**
34428         * @event contextmenu
34429         * Fires when a node is right clicked
34430         * @param {Node} node The node
34431         * @param {Roo.EventObject} e The event object
34432         */
34433         "contextmenu":true,
34434         /**
34435         * @event beforechildrenrendered
34436         * Fires right before the child nodes for a node are rendered
34437         * @param {Node} node The node
34438         */
34439         "beforechildrenrendered":true,
34440         /**
34441         * @event startdrag
34442         * Fires when a node starts being dragged
34443         * @param {Roo.tree.TreePanel} this
34444         * @param {Roo.tree.TreeNode} node
34445         * @param {event} e The raw browser event
34446         */ 
34447        "startdrag" : true,
34448        /**
34449         * @event enddrag
34450         * Fires when a drag operation is complete
34451         * @param {Roo.tree.TreePanel} this
34452         * @param {Roo.tree.TreeNode} node
34453         * @param {event} e The raw browser event
34454         */
34455        "enddrag" : true,
34456        /**
34457         * @event dragdrop
34458         * Fires when a dragged node is dropped on a valid DD target
34459         * @param {Roo.tree.TreePanel} this
34460         * @param {Roo.tree.TreeNode} node
34461         * @param {DD} dd The dd it was dropped on
34462         * @param {event} e The raw browser event
34463         */
34464        "dragdrop" : true,
34465        /**
34466         * @event beforenodedrop
34467         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34468         * passed to handlers has the following properties:<br />
34469         * <ul style="padding:5px;padding-left:16px;">
34470         * <li>tree - The TreePanel</li>
34471         * <li>target - The node being targeted for the drop</li>
34472         * <li>data - The drag data from the drag source</li>
34473         * <li>point - The point of the drop - append, above or below</li>
34474         * <li>source - The drag source</li>
34475         * <li>rawEvent - Raw mouse event</li>
34476         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34477         * to be inserted by setting them on this object.</li>
34478         * <li>cancel - Set this to true to cancel the drop.</li>
34479         * </ul>
34480         * @param {Object} dropEvent
34481         */
34482        "beforenodedrop" : true,
34483        /**
34484         * @event nodedrop
34485         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34486         * passed to handlers has the following properties:<br />
34487         * <ul style="padding:5px;padding-left:16px;">
34488         * <li>tree - The TreePanel</li>
34489         * <li>target - The node being targeted for the drop</li>
34490         * <li>data - The drag data from the drag source</li>
34491         * <li>point - The point of the drop - append, above or below</li>
34492         * <li>source - The drag source</li>
34493         * <li>rawEvent - Raw mouse event</li>
34494         * <li>dropNode - Dropped node(s).</li>
34495         * </ul>
34496         * @param {Object} dropEvent
34497         */
34498        "nodedrop" : true,
34499         /**
34500         * @event nodedragover
34501         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34502         * passed to handlers has the following properties:<br />
34503         * <ul style="padding:5px;padding-left:16px;">
34504         * <li>tree - The TreePanel</li>
34505         * <li>target - The node being targeted for the drop</li>
34506         * <li>data - The drag data from the drag source</li>
34507         * <li>point - The point of the drop - append, above or below</li>
34508         * <li>source - The drag source</li>
34509         * <li>rawEvent - Raw mouse event</li>
34510         * <li>dropNode - Drop node(s) provided by the source.</li>
34511         * <li>cancel - Set this to true to signal drop not allowed.</li>
34512         * </ul>
34513         * @param {Object} dragOverEvent
34514         */
34515        "nodedragover" : true,
34516        /**
34517         * @event appendnode
34518         * Fires when append node to the tree
34519         * @param {Roo.tree.TreePanel} this
34520         * @param {Roo.tree.TreeNode} node
34521         * @param {Number} index The index of the newly appended node
34522         */
34523        "appendnode" : true
34524         
34525     });
34526     if(this.singleExpand){
34527        this.on("beforeexpand", this.restrictExpand, this);
34528     }
34529     if (this.editor) {
34530         this.editor.tree = this;
34531         this.editor = Roo.factory(this.editor, Roo.tree);
34532     }
34533     
34534     if (this.selModel) {
34535         this.selModel = Roo.factory(this.selModel, Roo.tree);
34536     }
34537    
34538 };
34539 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34540     rootVisible : true,
34541     animate: Roo.enableFx,
34542     lines : true,
34543     enableDD : false,
34544     hlDrop : Roo.enableFx,
34545   
34546     renderer: false,
34547     
34548     rendererTip: false,
34549     // private
34550     restrictExpand : function(node){
34551         var p = node.parentNode;
34552         if(p){
34553             if(p.expandedChild && p.expandedChild.parentNode == p){
34554                 p.expandedChild.collapse();
34555             }
34556             p.expandedChild = node;
34557         }
34558     },
34559
34560     // private override
34561     setRootNode : function(node){
34562         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34563         if(!this.rootVisible){
34564             node.ui = new Roo.tree.RootTreeNodeUI(node);
34565         }
34566         return node;
34567     },
34568
34569     /**
34570      * Returns the container element for this TreePanel
34571      */
34572     getEl : function(){
34573         return this.el;
34574     },
34575
34576     /**
34577      * Returns the default TreeLoader for this TreePanel
34578      */
34579     getLoader : function(){
34580         return this.loader;
34581     },
34582
34583     /**
34584      * Expand all nodes
34585      */
34586     expandAll : function(){
34587         this.root.expand(true);
34588     },
34589
34590     /**
34591      * Collapse all nodes
34592      */
34593     collapseAll : function(){
34594         this.root.collapse(true);
34595     },
34596
34597     /**
34598      * Returns the selection model used by this TreePanel
34599      */
34600     getSelectionModel : function(){
34601         if(!this.selModel){
34602             this.selModel = new Roo.tree.DefaultSelectionModel();
34603         }
34604         return this.selModel;
34605     },
34606
34607     /**
34608      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34609      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34610      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34611      * @return {Array}
34612      */
34613     getChecked : function(a, startNode){
34614         startNode = startNode || this.root;
34615         var r = [];
34616         var f = function(){
34617             if(this.attributes.checked){
34618                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34619             }
34620         }
34621         startNode.cascade(f);
34622         return r;
34623     },
34624
34625     /**
34626      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34627      * @param {String} path
34628      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34629      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34630      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34631      */
34632     expandPath : function(path, attr, callback){
34633         attr = attr || "id";
34634         var keys = path.split(this.pathSeparator);
34635         var curNode = this.root;
34636         if(curNode.attributes[attr] != keys[1]){ // invalid root
34637             if(callback){
34638                 callback(false, null);
34639             }
34640             return;
34641         }
34642         var index = 1;
34643         var f = function(){
34644             if(++index == keys.length){
34645                 if(callback){
34646                     callback(true, curNode);
34647                 }
34648                 return;
34649             }
34650             var c = curNode.findChild(attr, keys[index]);
34651             if(!c){
34652                 if(callback){
34653                     callback(false, curNode);
34654                 }
34655                 return;
34656             }
34657             curNode = c;
34658             c.expand(false, false, f);
34659         };
34660         curNode.expand(false, false, f);
34661     },
34662
34663     /**
34664      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34665      * @param {String} path
34666      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34667      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34668      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34669      */
34670     selectPath : function(path, attr, callback){
34671         attr = attr || "id";
34672         var keys = path.split(this.pathSeparator);
34673         var v = keys.pop();
34674         if(keys.length > 0){
34675             var f = function(success, node){
34676                 if(success && node){
34677                     var n = node.findChild(attr, v);
34678                     if(n){
34679                         n.select();
34680                         if(callback){
34681                             callback(true, n);
34682                         }
34683                     }else if(callback){
34684                         callback(false, n);
34685                     }
34686                 }else{
34687                     if(callback){
34688                         callback(false, n);
34689                     }
34690                 }
34691             };
34692             this.expandPath(keys.join(this.pathSeparator), attr, f);
34693         }else{
34694             this.root.select();
34695             if(callback){
34696                 callback(true, this.root);
34697             }
34698         }
34699     },
34700
34701     getTreeEl : function(){
34702         return this.el;
34703     },
34704
34705     /**
34706      * Trigger rendering of this TreePanel
34707      */
34708     render : function(){
34709         if (this.innerCt) {
34710             return this; // stop it rendering more than once!!
34711         }
34712         
34713         this.innerCt = this.el.createChild({tag:"ul",
34714                cls:"x-tree-root-ct " +
34715                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34716
34717         if(this.containerScroll){
34718             Roo.dd.ScrollManager.register(this.el);
34719         }
34720         if((this.enableDD || this.enableDrop) && !this.dropZone){
34721            /**
34722             * The dropZone used by this tree if drop is enabled
34723             * @type Roo.tree.TreeDropZone
34724             */
34725              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34726                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34727            });
34728         }
34729         if((this.enableDD || this.enableDrag) && !this.dragZone){
34730            /**
34731             * The dragZone used by this tree if drag is enabled
34732             * @type Roo.tree.TreeDragZone
34733             */
34734             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34735                ddGroup: this.ddGroup || "TreeDD",
34736                scroll: this.ddScroll
34737            });
34738         }
34739         this.getSelectionModel().init(this);
34740         if (!this.root) {
34741             Roo.log("ROOT not set in tree");
34742             return this;
34743         }
34744         this.root.render();
34745         if(!this.rootVisible){
34746             this.root.renderChildren();
34747         }
34748         return this;
34749     }
34750 });/*
34751  * Based on:
34752  * Ext JS Library 1.1.1
34753  * Copyright(c) 2006-2007, Ext JS, LLC.
34754  *
34755  * Originally Released Under LGPL - original licence link has changed is not relivant.
34756  *
34757  * Fork - LGPL
34758  * <script type="text/javascript">
34759  */
34760  
34761
34762 /**
34763  * @class Roo.tree.DefaultSelectionModel
34764  * @extends Roo.util.Observable
34765  * The default single selection for a TreePanel.
34766  * @param {Object} cfg Configuration
34767  */
34768 Roo.tree.DefaultSelectionModel = function(cfg){
34769    this.selNode = null;
34770    
34771    
34772    
34773    this.addEvents({
34774        /**
34775         * @event selectionchange
34776         * Fires when the selected node changes
34777         * @param {DefaultSelectionModel} this
34778         * @param {TreeNode} node the new selection
34779         */
34780        "selectionchange" : true,
34781
34782        /**
34783         * @event beforeselect
34784         * Fires before the selected node changes, return false to cancel the change
34785         * @param {DefaultSelectionModel} this
34786         * @param {TreeNode} node the new selection
34787         * @param {TreeNode} node the old selection
34788         */
34789        "beforeselect" : true
34790    });
34791    
34792     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34793 };
34794
34795 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34796     init : function(tree){
34797         this.tree = tree;
34798         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34799         tree.on("click", this.onNodeClick, this);
34800     },
34801     
34802     onNodeClick : function(node, e){
34803         if (e.ctrlKey && this.selNode == node)  {
34804             this.unselect(node);
34805             return;
34806         }
34807         this.select(node);
34808     },
34809     
34810     /**
34811      * Select a node.
34812      * @param {TreeNode} node The node to select
34813      * @return {TreeNode} The selected node
34814      */
34815     select : function(node){
34816         var last = this.selNode;
34817         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34818             if(last){
34819                 last.ui.onSelectedChange(false);
34820             }
34821             this.selNode = node;
34822             node.ui.onSelectedChange(true);
34823             this.fireEvent("selectionchange", this, node, last);
34824         }
34825         return node;
34826     },
34827     
34828     /**
34829      * Deselect a node.
34830      * @param {TreeNode} node The node to unselect
34831      */
34832     unselect : function(node){
34833         if(this.selNode == node){
34834             this.clearSelections();
34835         }    
34836     },
34837     
34838     /**
34839      * Clear all selections
34840      */
34841     clearSelections : function(){
34842         var n = this.selNode;
34843         if(n){
34844             n.ui.onSelectedChange(false);
34845             this.selNode = null;
34846             this.fireEvent("selectionchange", this, null);
34847         }
34848         return n;
34849     },
34850     
34851     /**
34852      * Get the selected node
34853      * @return {TreeNode} The selected node
34854      */
34855     getSelectedNode : function(){
34856         return this.selNode;    
34857     },
34858     
34859     /**
34860      * Returns true if the node is selected
34861      * @param {TreeNode} node The node to check
34862      * @return {Boolean}
34863      */
34864     isSelected : function(node){
34865         return this.selNode == node;  
34866     },
34867
34868     /**
34869      * Selects the node above the selected node in the tree, intelligently walking the nodes
34870      * @return TreeNode The new selection
34871      */
34872     selectPrevious : function(){
34873         var s = this.selNode || this.lastSelNode;
34874         if(!s){
34875             return null;
34876         }
34877         var ps = s.previousSibling;
34878         if(ps){
34879             if(!ps.isExpanded() || ps.childNodes.length < 1){
34880                 return this.select(ps);
34881             } else{
34882                 var lc = ps.lastChild;
34883                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34884                     lc = lc.lastChild;
34885                 }
34886                 return this.select(lc);
34887             }
34888         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34889             return this.select(s.parentNode);
34890         }
34891         return null;
34892     },
34893
34894     /**
34895      * Selects the node above the selected node in the tree, intelligently walking the nodes
34896      * @return TreeNode The new selection
34897      */
34898     selectNext : function(){
34899         var s = this.selNode || this.lastSelNode;
34900         if(!s){
34901             return null;
34902         }
34903         if(s.firstChild && s.isExpanded()){
34904              return this.select(s.firstChild);
34905          }else if(s.nextSibling){
34906              return this.select(s.nextSibling);
34907          }else if(s.parentNode){
34908             var newS = null;
34909             s.parentNode.bubble(function(){
34910                 if(this.nextSibling){
34911                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34912                     return false;
34913                 }
34914             });
34915             return newS;
34916          }
34917         return null;
34918     },
34919
34920     onKeyDown : function(e){
34921         var s = this.selNode || this.lastSelNode;
34922         // undesirable, but required
34923         var sm = this;
34924         if(!s){
34925             return;
34926         }
34927         var k = e.getKey();
34928         switch(k){
34929              case e.DOWN:
34930                  e.stopEvent();
34931                  this.selectNext();
34932              break;
34933              case e.UP:
34934                  e.stopEvent();
34935                  this.selectPrevious();
34936              break;
34937              case e.RIGHT:
34938                  e.preventDefault();
34939                  if(s.hasChildNodes()){
34940                      if(!s.isExpanded()){
34941                          s.expand();
34942                      }else if(s.firstChild){
34943                          this.select(s.firstChild, e);
34944                      }
34945                  }
34946              break;
34947              case e.LEFT:
34948                  e.preventDefault();
34949                  if(s.hasChildNodes() && s.isExpanded()){
34950                      s.collapse();
34951                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
34952                      this.select(s.parentNode, e);
34953                  }
34954              break;
34955         };
34956     }
34957 });
34958
34959 /**
34960  * @class Roo.tree.MultiSelectionModel
34961  * @extends Roo.util.Observable
34962  * Multi selection for a TreePanel.
34963  * @param {Object} cfg Configuration
34964  */
34965 Roo.tree.MultiSelectionModel = function(){
34966    this.selNodes = [];
34967    this.selMap = {};
34968    this.addEvents({
34969        /**
34970         * @event selectionchange
34971         * Fires when the selected nodes change
34972         * @param {MultiSelectionModel} this
34973         * @param {Array} nodes Array of the selected nodes
34974         */
34975        "selectionchange" : true
34976    });
34977    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
34978    
34979 };
34980
34981 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
34982     init : function(tree){
34983         this.tree = tree;
34984         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34985         tree.on("click", this.onNodeClick, this);
34986     },
34987     
34988     onNodeClick : function(node, e){
34989         this.select(node, e, e.ctrlKey);
34990     },
34991     
34992     /**
34993      * Select a node.
34994      * @param {TreeNode} node The node to select
34995      * @param {EventObject} e (optional) An event associated with the selection
34996      * @param {Boolean} keepExisting True to retain existing selections
34997      * @return {TreeNode} The selected node
34998      */
34999     select : function(node, e, keepExisting){
35000         if(keepExisting !== true){
35001             this.clearSelections(true);
35002         }
35003         if(this.isSelected(node)){
35004             this.lastSelNode = node;
35005             return node;
35006         }
35007         this.selNodes.push(node);
35008         this.selMap[node.id] = node;
35009         this.lastSelNode = node;
35010         node.ui.onSelectedChange(true);
35011         this.fireEvent("selectionchange", this, this.selNodes);
35012         return node;
35013     },
35014     
35015     /**
35016      * Deselect a node.
35017      * @param {TreeNode} node The node to unselect
35018      */
35019     unselect : function(node){
35020         if(this.selMap[node.id]){
35021             node.ui.onSelectedChange(false);
35022             var sn = this.selNodes;
35023             var index = -1;
35024             if(sn.indexOf){
35025                 index = sn.indexOf(node);
35026             }else{
35027                 for(var i = 0, len = sn.length; i < len; i++){
35028                     if(sn[i] == node){
35029                         index = i;
35030                         break;
35031                     }
35032                 }
35033             }
35034             if(index != -1){
35035                 this.selNodes.splice(index, 1);
35036             }
35037             delete this.selMap[node.id];
35038             this.fireEvent("selectionchange", this, this.selNodes);
35039         }
35040     },
35041     
35042     /**
35043      * Clear all selections
35044      */
35045     clearSelections : function(suppressEvent){
35046         var sn = this.selNodes;
35047         if(sn.length > 0){
35048             for(var i = 0, len = sn.length; i < len; i++){
35049                 sn[i].ui.onSelectedChange(false);
35050             }
35051             this.selNodes = [];
35052             this.selMap = {};
35053             if(suppressEvent !== true){
35054                 this.fireEvent("selectionchange", this, this.selNodes);
35055             }
35056         }
35057     },
35058     
35059     /**
35060      * Returns true if the node is selected
35061      * @param {TreeNode} node The node to check
35062      * @return {Boolean}
35063      */
35064     isSelected : function(node){
35065         return this.selMap[node.id] ? true : false;  
35066     },
35067     
35068     /**
35069      * Returns an array of the selected nodes
35070      * @return {Array}
35071      */
35072     getSelectedNodes : function(){
35073         return this.selNodes;    
35074     },
35075
35076     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35077
35078     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35079
35080     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35081 });/*
35082  * Based on:
35083  * Ext JS Library 1.1.1
35084  * Copyright(c) 2006-2007, Ext JS, LLC.
35085  *
35086  * Originally Released Under LGPL - original licence link has changed is not relivant.
35087  *
35088  * Fork - LGPL
35089  * <script type="text/javascript">
35090  */
35091  
35092 /**
35093  * @class Roo.tree.TreeNode
35094  * @extends Roo.data.Node
35095  * @cfg {String} text The text for this node
35096  * @cfg {Boolean} expanded true to start the node expanded
35097  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35098  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35099  * @cfg {Boolean} disabled true to start the node disabled
35100  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35101  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35102  * @cfg {String} cls A css class to be added to the node
35103  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35104  * @cfg {String} href URL of the link used for the node (defaults to #)
35105  * @cfg {String} hrefTarget target frame for the link
35106  * @cfg {String} qtip An Ext QuickTip for the node
35107  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35108  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35109  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35110  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35111  * (defaults to undefined with no checkbox rendered)
35112  * @constructor
35113  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35114  */
35115 Roo.tree.TreeNode = function(attributes){
35116     attributes = attributes || {};
35117     if(typeof attributes == "string"){
35118         attributes = {text: attributes};
35119     }
35120     this.childrenRendered = false;
35121     this.rendered = false;
35122     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35123     this.expanded = attributes.expanded === true;
35124     this.isTarget = attributes.isTarget !== false;
35125     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35126     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35127
35128     /**
35129      * Read-only. The text for this node. To change it use setText().
35130      * @type String
35131      */
35132     this.text = attributes.text;
35133     /**
35134      * True if this node is disabled.
35135      * @type Boolean
35136      */
35137     this.disabled = attributes.disabled === true;
35138
35139     this.addEvents({
35140         /**
35141         * @event textchange
35142         * Fires when the text for this node is changed
35143         * @param {Node} this This node
35144         * @param {String} text The new text
35145         * @param {String} oldText The old text
35146         */
35147         "textchange" : true,
35148         /**
35149         * @event beforeexpand
35150         * Fires before this node is expanded, return false to cancel.
35151         * @param {Node} this This node
35152         * @param {Boolean} deep
35153         * @param {Boolean} anim
35154         */
35155         "beforeexpand" : true,
35156         /**
35157         * @event beforecollapse
35158         * Fires before this node is collapsed, return false to cancel.
35159         * @param {Node} this This node
35160         * @param {Boolean} deep
35161         * @param {Boolean} anim
35162         */
35163         "beforecollapse" : true,
35164         /**
35165         * @event expand
35166         * Fires when this node is expanded
35167         * @param {Node} this This node
35168         */
35169         "expand" : true,
35170         /**
35171         * @event disabledchange
35172         * Fires when the disabled status of this node changes
35173         * @param {Node} this This node
35174         * @param {Boolean} disabled
35175         */
35176         "disabledchange" : true,
35177         /**
35178         * @event collapse
35179         * Fires when this node is collapsed
35180         * @param {Node} this This node
35181         */
35182         "collapse" : true,
35183         /**
35184         * @event beforeclick
35185         * Fires before click processing. Return false to cancel the default action.
35186         * @param {Node} this This node
35187         * @param {Roo.EventObject} e The event object
35188         */
35189         "beforeclick":true,
35190         /**
35191         * @event checkchange
35192         * Fires when a node with a checkbox's checked property changes
35193         * @param {Node} this This node
35194         * @param {Boolean} checked
35195         */
35196         "checkchange":true,
35197         /**
35198         * @event click
35199         * Fires when this node is clicked
35200         * @param {Node} this This node
35201         * @param {Roo.EventObject} e The event object
35202         */
35203         "click":true,
35204         /**
35205         * @event dblclick
35206         * Fires when this node is double clicked
35207         * @param {Node} this This node
35208         * @param {Roo.EventObject} e The event object
35209         */
35210         "dblclick":true,
35211         /**
35212         * @event contextmenu
35213         * Fires when this node is right clicked
35214         * @param {Node} this This node
35215         * @param {Roo.EventObject} e The event object
35216         */
35217         "contextmenu":true,
35218         /**
35219         * @event beforechildrenrendered
35220         * Fires right before the child nodes for this node are rendered
35221         * @param {Node} this This node
35222         */
35223         "beforechildrenrendered":true
35224     });
35225
35226     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35227
35228     /**
35229      * Read-only. The UI for this node
35230      * @type TreeNodeUI
35231      */
35232     this.ui = new uiClass(this);
35233     
35234     // finally support items[]
35235     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35236         return;
35237     }
35238     
35239     
35240     Roo.each(this.attributes.items, function(c) {
35241         this.appendChild(Roo.factory(c,Roo.Tree));
35242     }, this);
35243     delete this.attributes.items;
35244     
35245     
35246     
35247 };
35248 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35249     preventHScroll: true,
35250     /**
35251      * Returns true if this node is expanded
35252      * @return {Boolean}
35253      */
35254     isExpanded : function(){
35255         return this.expanded;
35256     },
35257
35258     /**
35259      * Returns the UI object for this node
35260      * @return {TreeNodeUI}
35261      */
35262     getUI : function(){
35263         return this.ui;
35264     },
35265
35266     // private override
35267     setFirstChild : function(node){
35268         var of = this.firstChild;
35269         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35270         if(this.childrenRendered && of && node != of){
35271             of.renderIndent(true, true);
35272         }
35273         if(this.rendered){
35274             this.renderIndent(true, true);
35275         }
35276     },
35277
35278     // private override
35279     setLastChild : function(node){
35280         var ol = this.lastChild;
35281         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35282         if(this.childrenRendered && ol && node != ol){
35283             ol.renderIndent(true, true);
35284         }
35285         if(this.rendered){
35286             this.renderIndent(true, true);
35287         }
35288     },
35289
35290     // these methods are overridden to provide lazy rendering support
35291     // private override
35292     appendChild : function()
35293     {
35294         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35295         if(node && this.childrenRendered){
35296             node.render();
35297         }
35298         this.ui.updateExpandIcon();
35299         return node;
35300     },
35301
35302     // private override
35303     removeChild : function(node){
35304         this.ownerTree.getSelectionModel().unselect(node);
35305         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35306         // if it's been rendered remove dom node
35307         if(this.childrenRendered){
35308             node.ui.remove();
35309         }
35310         if(this.childNodes.length < 1){
35311             this.collapse(false, false);
35312         }else{
35313             this.ui.updateExpandIcon();
35314         }
35315         if(!this.firstChild) {
35316             this.childrenRendered = false;
35317         }
35318         return node;
35319     },
35320
35321     // private override
35322     insertBefore : function(node, refNode){
35323         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35324         if(newNode && refNode && this.childrenRendered){
35325             node.render();
35326         }
35327         this.ui.updateExpandIcon();
35328         return newNode;
35329     },
35330
35331     /**
35332      * Sets the text for this node
35333      * @param {String} text
35334      */
35335     setText : function(text){
35336         var oldText = this.text;
35337         this.text = text;
35338         this.attributes.text = text;
35339         if(this.rendered){ // event without subscribing
35340             this.ui.onTextChange(this, text, oldText);
35341         }
35342         this.fireEvent("textchange", this, text, oldText);
35343     },
35344
35345     /**
35346      * Triggers selection of this node
35347      */
35348     select : function(){
35349         this.getOwnerTree().getSelectionModel().select(this);
35350     },
35351
35352     /**
35353      * Triggers deselection of this node
35354      */
35355     unselect : function(){
35356         this.getOwnerTree().getSelectionModel().unselect(this);
35357     },
35358
35359     /**
35360      * Returns true if this node is selected
35361      * @return {Boolean}
35362      */
35363     isSelected : function(){
35364         return this.getOwnerTree().getSelectionModel().isSelected(this);
35365     },
35366
35367     /**
35368      * Expand this node.
35369      * @param {Boolean} deep (optional) True to expand all children as well
35370      * @param {Boolean} anim (optional) false to cancel the default animation
35371      * @param {Function} callback (optional) A callback to be called when
35372      * expanding this node completes (does not wait for deep expand to complete).
35373      * Called with 1 parameter, this node.
35374      */
35375     expand : function(deep, anim, callback){
35376         if(!this.expanded){
35377             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35378                 return;
35379             }
35380             if(!this.childrenRendered){
35381                 this.renderChildren();
35382             }
35383             this.expanded = true;
35384             
35385             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35386                 this.ui.animExpand(function(){
35387                     this.fireEvent("expand", this);
35388                     if(typeof callback == "function"){
35389                         callback(this);
35390                     }
35391                     if(deep === true){
35392                         this.expandChildNodes(true);
35393                     }
35394                 }.createDelegate(this));
35395                 return;
35396             }else{
35397                 this.ui.expand();
35398                 this.fireEvent("expand", this);
35399                 if(typeof callback == "function"){
35400                     callback(this);
35401                 }
35402             }
35403         }else{
35404            if(typeof callback == "function"){
35405                callback(this);
35406            }
35407         }
35408         if(deep === true){
35409             this.expandChildNodes(true);
35410         }
35411     },
35412
35413     isHiddenRoot : function(){
35414         return this.isRoot && !this.getOwnerTree().rootVisible;
35415     },
35416
35417     /**
35418      * Collapse this node.
35419      * @param {Boolean} deep (optional) True to collapse all children as well
35420      * @param {Boolean} anim (optional) false to cancel the default animation
35421      */
35422     collapse : function(deep, anim){
35423         if(this.expanded && !this.isHiddenRoot()){
35424             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35425                 return;
35426             }
35427             this.expanded = false;
35428             if((this.getOwnerTree().animate && anim !== false) || anim){
35429                 this.ui.animCollapse(function(){
35430                     this.fireEvent("collapse", this);
35431                     if(deep === true){
35432                         this.collapseChildNodes(true);
35433                     }
35434                 }.createDelegate(this));
35435                 return;
35436             }else{
35437                 this.ui.collapse();
35438                 this.fireEvent("collapse", this);
35439             }
35440         }
35441         if(deep === true){
35442             var cs = this.childNodes;
35443             for(var i = 0, len = cs.length; i < len; i++) {
35444                 cs[i].collapse(true, false);
35445             }
35446         }
35447     },
35448
35449     // private
35450     delayedExpand : function(delay){
35451         if(!this.expandProcId){
35452             this.expandProcId = this.expand.defer(delay, this);
35453         }
35454     },
35455
35456     // private
35457     cancelExpand : function(){
35458         if(this.expandProcId){
35459             clearTimeout(this.expandProcId);
35460         }
35461         this.expandProcId = false;
35462     },
35463
35464     /**
35465      * Toggles expanded/collapsed state of the node
35466      */
35467     toggle : function(){
35468         if(this.expanded){
35469             this.collapse();
35470         }else{
35471             this.expand();
35472         }
35473     },
35474
35475     /**
35476      * Ensures all parent nodes are expanded
35477      */
35478     ensureVisible : function(callback){
35479         var tree = this.getOwnerTree();
35480         tree.expandPath(this.parentNode.getPath(), false, function(){
35481             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35482             Roo.callback(callback);
35483         }.createDelegate(this));
35484     },
35485
35486     /**
35487      * Expand all child nodes
35488      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35489      */
35490     expandChildNodes : function(deep){
35491         var cs = this.childNodes;
35492         for(var i = 0, len = cs.length; i < len; i++) {
35493                 cs[i].expand(deep);
35494         }
35495     },
35496
35497     /**
35498      * Collapse all child nodes
35499      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35500      */
35501     collapseChildNodes : function(deep){
35502         var cs = this.childNodes;
35503         for(var i = 0, len = cs.length; i < len; i++) {
35504                 cs[i].collapse(deep);
35505         }
35506     },
35507
35508     /**
35509      * Disables this node
35510      */
35511     disable : function(){
35512         this.disabled = true;
35513         this.unselect();
35514         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35515             this.ui.onDisableChange(this, true);
35516         }
35517         this.fireEvent("disabledchange", this, true);
35518     },
35519
35520     /**
35521      * Enables this node
35522      */
35523     enable : function(){
35524         this.disabled = false;
35525         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35526             this.ui.onDisableChange(this, false);
35527         }
35528         this.fireEvent("disabledchange", this, false);
35529     },
35530
35531     // private
35532     renderChildren : function(suppressEvent){
35533         if(suppressEvent !== false){
35534             this.fireEvent("beforechildrenrendered", this);
35535         }
35536         var cs = this.childNodes;
35537         for(var i = 0, len = cs.length; i < len; i++){
35538             cs[i].render(true);
35539         }
35540         this.childrenRendered = true;
35541     },
35542
35543     // private
35544     sort : function(fn, scope){
35545         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35546         if(this.childrenRendered){
35547             var cs = this.childNodes;
35548             for(var i = 0, len = cs.length; i < len; i++){
35549                 cs[i].render(true);
35550             }
35551         }
35552     },
35553
35554     // private
35555     render : function(bulkRender){
35556         this.ui.render(bulkRender);
35557         if(!this.rendered){
35558             this.rendered = true;
35559             if(this.expanded){
35560                 this.expanded = false;
35561                 this.expand(false, false);
35562             }
35563         }
35564     },
35565
35566     // private
35567     renderIndent : function(deep, refresh){
35568         if(refresh){
35569             this.ui.childIndent = null;
35570         }
35571         this.ui.renderIndent();
35572         if(deep === true && this.childrenRendered){
35573             var cs = this.childNodes;
35574             for(var i = 0, len = cs.length; i < len; i++){
35575                 cs[i].renderIndent(true, refresh);
35576             }
35577         }
35578     }
35579 });/*
35580  * Based on:
35581  * Ext JS Library 1.1.1
35582  * Copyright(c) 2006-2007, Ext JS, LLC.
35583  *
35584  * Originally Released Under LGPL - original licence link has changed is not relivant.
35585  *
35586  * Fork - LGPL
35587  * <script type="text/javascript">
35588  */
35589  
35590 /**
35591  * @class Roo.tree.AsyncTreeNode
35592  * @extends Roo.tree.TreeNode
35593  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35594  * @constructor
35595  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35596  */
35597  Roo.tree.AsyncTreeNode = function(config){
35598     this.loaded = false;
35599     this.loading = false;
35600     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35601     /**
35602     * @event beforeload
35603     * Fires before this node is loaded, return false to cancel
35604     * @param {Node} this This node
35605     */
35606     this.addEvents({'beforeload':true, 'load': true});
35607     /**
35608     * @event load
35609     * Fires when this node is loaded
35610     * @param {Node} this This node
35611     */
35612     /**
35613      * The loader used by this node (defaults to using the tree's defined loader)
35614      * @type TreeLoader
35615      * @property loader
35616      */
35617 };
35618 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35619     expand : function(deep, anim, callback){
35620         if(this.loading){ // if an async load is already running, waiting til it's done
35621             var timer;
35622             var f = function(){
35623                 if(!this.loading){ // done loading
35624                     clearInterval(timer);
35625                     this.expand(deep, anim, callback);
35626                 }
35627             }.createDelegate(this);
35628             timer = setInterval(f, 200);
35629             return;
35630         }
35631         if(!this.loaded){
35632             if(this.fireEvent("beforeload", this) === false){
35633                 return;
35634             }
35635             this.loading = true;
35636             this.ui.beforeLoad(this);
35637             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35638             if(loader){
35639                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35640                 return;
35641             }
35642         }
35643         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35644     },
35645     
35646     /**
35647      * Returns true if this node is currently loading
35648      * @return {Boolean}
35649      */
35650     isLoading : function(){
35651         return this.loading;  
35652     },
35653     
35654     loadComplete : function(deep, anim, callback){
35655         this.loading = false;
35656         this.loaded = true;
35657         this.ui.afterLoad(this);
35658         this.fireEvent("load", this);
35659         this.expand(deep, anim, callback);
35660     },
35661     
35662     /**
35663      * Returns true if this node has been loaded
35664      * @return {Boolean}
35665      */
35666     isLoaded : function(){
35667         return this.loaded;
35668     },
35669     
35670     hasChildNodes : function(){
35671         if(!this.isLeaf() && !this.loaded){
35672             return true;
35673         }else{
35674             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35675         }
35676     },
35677
35678     /**
35679      * Trigger a reload for this node
35680      * @param {Function} callback
35681      */
35682     reload : function(callback){
35683         this.collapse(false, false);
35684         while(this.firstChild){
35685             this.removeChild(this.firstChild);
35686         }
35687         this.childrenRendered = false;
35688         this.loaded = false;
35689         if(this.isHiddenRoot()){
35690             this.expanded = false;
35691         }
35692         this.expand(false, false, callback);
35693     }
35694 });/*
35695  * Based on:
35696  * Ext JS Library 1.1.1
35697  * Copyright(c) 2006-2007, Ext JS, LLC.
35698  *
35699  * Originally Released Under LGPL - original licence link has changed is not relivant.
35700  *
35701  * Fork - LGPL
35702  * <script type="text/javascript">
35703  */
35704  
35705 /**
35706  * @class Roo.tree.TreeNodeUI
35707  * @constructor
35708  * @param {Object} node The node to render
35709  * The TreeNode UI implementation is separate from the
35710  * tree implementation. Unless you are customizing the tree UI,
35711  * you should never have to use this directly.
35712  */
35713 Roo.tree.TreeNodeUI = function(node){
35714     this.node = node;
35715     this.rendered = false;
35716     this.animating = false;
35717     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35718 };
35719
35720 Roo.tree.TreeNodeUI.prototype = {
35721     removeChild : function(node){
35722         if(this.rendered){
35723             this.ctNode.removeChild(node.ui.getEl());
35724         }
35725     },
35726
35727     beforeLoad : function(){
35728          this.addClass("x-tree-node-loading");
35729     },
35730
35731     afterLoad : function(){
35732          this.removeClass("x-tree-node-loading");
35733     },
35734
35735     onTextChange : function(node, text, oldText){
35736         if(this.rendered){
35737             this.textNode.innerHTML = text;
35738         }
35739     },
35740
35741     onDisableChange : function(node, state){
35742         this.disabled = state;
35743         if(state){
35744             this.addClass("x-tree-node-disabled");
35745         }else{
35746             this.removeClass("x-tree-node-disabled");
35747         }
35748     },
35749
35750     onSelectedChange : function(state){
35751         if(state){
35752             this.focus();
35753             this.addClass("x-tree-selected");
35754         }else{
35755             //this.blur();
35756             this.removeClass("x-tree-selected");
35757         }
35758     },
35759
35760     onMove : function(tree, node, oldParent, newParent, index, refNode){
35761         this.childIndent = null;
35762         if(this.rendered){
35763             var targetNode = newParent.ui.getContainer();
35764             if(!targetNode){//target not rendered
35765                 this.holder = document.createElement("div");
35766                 this.holder.appendChild(this.wrap);
35767                 return;
35768             }
35769             var insertBefore = refNode ? refNode.ui.getEl() : null;
35770             if(insertBefore){
35771                 targetNode.insertBefore(this.wrap, insertBefore);
35772             }else{
35773                 targetNode.appendChild(this.wrap);
35774             }
35775             this.node.renderIndent(true);
35776         }
35777     },
35778
35779     addClass : function(cls){
35780         if(this.elNode){
35781             Roo.fly(this.elNode).addClass(cls);
35782         }
35783     },
35784
35785     removeClass : function(cls){
35786         if(this.elNode){
35787             Roo.fly(this.elNode).removeClass(cls);
35788         }
35789     },
35790
35791     remove : function(){
35792         if(this.rendered){
35793             this.holder = document.createElement("div");
35794             this.holder.appendChild(this.wrap);
35795         }
35796     },
35797
35798     fireEvent : function(){
35799         return this.node.fireEvent.apply(this.node, arguments);
35800     },
35801
35802     initEvents : function(){
35803         this.node.on("move", this.onMove, this);
35804         var E = Roo.EventManager;
35805         var a = this.anchor;
35806
35807         var el = Roo.fly(a, '_treeui');
35808
35809         if(Roo.isOpera){ // opera render bug ignores the CSS
35810             el.setStyle("text-decoration", "none");
35811         }
35812
35813         el.on("click", this.onClick, this);
35814         el.on("dblclick", this.onDblClick, this);
35815
35816         if(this.checkbox){
35817             Roo.EventManager.on(this.checkbox,
35818                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35819         }
35820
35821         el.on("contextmenu", this.onContextMenu, this);
35822
35823         var icon = Roo.fly(this.iconNode);
35824         icon.on("click", this.onClick, this);
35825         icon.on("dblclick", this.onDblClick, this);
35826         icon.on("contextmenu", this.onContextMenu, this);
35827         E.on(this.ecNode, "click", this.ecClick, this, true);
35828
35829         if(this.node.disabled){
35830             this.addClass("x-tree-node-disabled");
35831         }
35832         if(this.node.hidden){
35833             this.addClass("x-tree-node-disabled");
35834         }
35835         var ot = this.node.getOwnerTree();
35836         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35837         if(dd && (!this.node.isRoot || ot.rootVisible)){
35838             Roo.dd.Registry.register(this.elNode, {
35839                 node: this.node,
35840                 handles: this.getDDHandles(),
35841                 isHandle: false
35842             });
35843         }
35844     },
35845
35846     getDDHandles : function(){
35847         return [this.iconNode, this.textNode];
35848     },
35849
35850     hide : function(){
35851         if(this.rendered){
35852             this.wrap.style.display = "none";
35853         }
35854     },
35855
35856     show : function(){
35857         if(this.rendered){
35858             this.wrap.style.display = "";
35859         }
35860     },
35861
35862     onContextMenu : function(e){
35863         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35864             e.preventDefault();
35865             this.focus();
35866             this.fireEvent("contextmenu", this.node, e);
35867         }
35868     },
35869
35870     onClick : function(e){
35871         if(this.dropping){
35872             e.stopEvent();
35873             return;
35874         }
35875         if(this.fireEvent("beforeclick", this.node, e) !== false){
35876             if(!this.disabled && this.node.attributes.href){
35877                 this.fireEvent("click", this.node, e);
35878                 return;
35879             }
35880             e.preventDefault();
35881             if(this.disabled){
35882                 return;
35883             }
35884
35885             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35886                 this.node.toggle();
35887             }
35888
35889             this.fireEvent("click", this.node, e);
35890         }else{
35891             e.stopEvent();
35892         }
35893     },
35894
35895     onDblClick : function(e){
35896         e.preventDefault();
35897         if(this.disabled){
35898             return;
35899         }
35900         if(this.checkbox){
35901             this.toggleCheck();
35902         }
35903         if(!this.animating && this.node.hasChildNodes()){
35904             this.node.toggle();
35905         }
35906         this.fireEvent("dblclick", this.node, e);
35907     },
35908
35909     onCheckChange : function(){
35910         var checked = this.checkbox.checked;
35911         this.node.attributes.checked = checked;
35912         this.fireEvent('checkchange', this.node, checked);
35913     },
35914
35915     ecClick : function(e){
35916         if(!this.animating && this.node.hasChildNodes()){
35917             this.node.toggle();
35918         }
35919     },
35920
35921     startDrop : function(){
35922         this.dropping = true;
35923     },
35924
35925     // delayed drop so the click event doesn't get fired on a drop
35926     endDrop : function(){
35927        setTimeout(function(){
35928            this.dropping = false;
35929        }.createDelegate(this), 50);
35930     },
35931
35932     expand : function(){
35933         this.updateExpandIcon();
35934         this.ctNode.style.display = "";
35935     },
35936
35937     focus : function(){
35938         if(!this.node.preventHScroll){
35939             try{this.anchor.focus();
35940             }catch(e){}
35941         }else if(!Roo.isIE){
35942             try{
35943                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
35944                 var l = noscroll.scrollLeft;
35945                 this.anchor.focus();
35946                 noscroll.scrollLeft = l;
35947             }catch(e){}
35948         }
35949     },
35950
35951     toggleCheck : function(value){
35952         var cb = this.checkbox;
35953         if(cb){
35954             cb.checked = (value === undefined ? !cb.checked : value);
35955         }
35956     },
35957
35958     blur : function(){
35959         try{
35960             this.anchor.blur();
35961         }catch(e){}
35962     },
35963
35964     animExpand : function(callback){
35965         var ct = Roo.get(this.ctNode);
35966         ct.stopFx();
35967         if(!this.node.hasChildNodes()){
35968             this.updateExpandIcon();
35969             this.ctNode.style.display = "";
35970             Roo.callback(callback);
35971             return;
35972         }
35973         this.animating = true;
35974         this.updateExpandIcon();
35975
35976         ct.slideIn('t', {
35977            callback : function(){
35978                this.animating = false;
35979                Roo.callback(callback);
35980             },
35981             scope: this,
35982             duration: this.node.ownerTree.duration || .25
35983         });
35984     },
35985
35986     highlight : function(){
35987         var tree = this.node.getOwnerTree();
35988         Roo.fly(this.wrap).highlight(
35989             tree.hlColor || "C3DAF9",
35990             {endColor: tree.hlBaseColor}
35991         );
35992     },
35993
35994     collapse : function(){
35995         this.updateExpandIcon();
35996         this.ctNode.style.display = "none";
35997     },
35998
35999     animCollapse : function(callback){
36000         var ct = Roo.get(this.ctNode);
36001         ct.enableDisplayMode('block');
36002         ct.stopFx();
36003
36004         this.animating = true;
36005         this.updateExpandIcon();
36006
36007         ct.slideOut('t', {
36008             callback : function(){
36009                this.animating = false;
36010                Roo.callback(callback);
36011             },
36012             scope: this,
36013             duration: this.node.ownerTree.duration || .25
36014         });
36015     },
36016
36017     getContainer : function(){
36018         return this.ctNode;
36019     },
36020
36021     getEl : function(){
36022         return this.wrap;
36023     },
36024
36025     appendDDGhost : function(ghostNode){
36026         ghostNode.appendChild(this.elNode.cloneNode(true));
36027     },
36028
36029     getDDRepairXY : function(){
36030         return Roo.lib.Dom.getXY(this.iconNode);
36031     },
36032
36033     onRender : function(){
36034         this.render();
36035     },
36036
36037     render : function(bulkRender){
36038         var n = this.node, a = n.attributes;
36039         var targetNode = n.parentNode ?
36040               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36041
36042         if(!this.rendered){
36043             this.rendered = true;
36044
36045             this.renderElements(n, a, targetNode, bulkRender);
36046
36047             if(a.qtip){
36048                if(this.textNode.setAttributeNS){
36049                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36050                    if(a.qtipTitle){
36051                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36052                    }
36053                }else{
36054                    this.textNode.setAttribute("ext:qtip", a.qtip);
36055                    if(a.qtipTitle){
36056                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36057                    }
36058                }
36059             }else if(a.qtipCfg){
36060                 a.qtipCfg.target = Roo.id(this.textNode);
36061                 Roo.QuickTips.register(a.qtipCfg);
36062             }
36063             this.initEvents();
36064             if(!this.node.expanded){
36065                 this.updateExpandIcon();
36066             }
36067         }else{
36068             if(bulkRender === true) {
36069                 targetNode.appendChild(this.wrap);
36070             }
36071         }
36072     },
36073
36074     renderElements : function(n, a, targetNode, bulkRender)
36075     {
36076         // add some indent caching, this helps performance when rendering a large tree
36077         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36078         var t = n.getOwnerTree();
36079         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36080         if (typeof(n.attributes.html) != 'undefined') {
36081             txt = n.attributes.html;
36082         }
36083         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36084         var cb = typeof a.checked == 'boolean';
36085         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36086         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36087             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36088             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36089             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36090             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36091             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36092              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36093                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36094             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36095             "</li>"];
36096
36097         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36098             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36099                                 n.nextSibling.ui.getEl(), buf.join(""));
36100         }else{
36101             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36102         }
36103
36104         this.elNode = this.wrap.childNodes[0];
36105         this.ctNode = this.wrap.childNodes[1];
36106         var cs = this.elNode.childNodes;
36107         this.indentNode = cs[0];
36108         this.ecNode = cs[1];
36109         this.iconNode = cs[2];
36110         var index = 3;
36111         if(cb){
36112             this.checkbox = cs[3];
36113             index++;
36114         }
36115         this.anchor = cs[index];
36116         this.textNode = cs[index].firstChild;
36117     },
36118
36119     getAnchor : function(){
36120         return this.anchor;
36121     },
36122
36123     getTextEl : function(){
36124         return this.textNode;
36125     },
36126
36127     getIconEl : function(){
36128         return this.iconNode;
36129     },
36130
36131     isChecked : function(){
36132         return this.checkbox ? this.checkbox.checked : false;
36133     },
36134
36135     updateExpandIcon : function(){
36136         if(this.rendered){
36137             var n = this.node, c1, c2;
36138             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36139             var hasChild = n.hasChildNodes();
36140             if(hasChild){
36141                 if(n.expanded){
36142                     cls += "-minus";
36143                     c1 = "x-tree-node-collapsed";
36144                     c2 = "x-tree-node-expanded";
36145                 }else{
36146                     cls += "-plus";
36147                     c1 = "x-tree-node-expanded";
36148                     c2 = "x-tree-node-collapsed";
36149                 }
36150                 if(this.wasLeaf){
36151                     this.removeClass("x-tree-node-leaf");
36152                     this.wasLeaf = false;
36153                 }
36154                 if(this.c1 != c1 || this.c2 != c2){
36155                     Roo.fly(this.elNode).replaceClass(c1, c2);
36156                     this.c1 = c1; this.c2 = c2;
36157                 }
36158             }else{
36159                 // this changes non-leafs into leafs if they have no children.
36160                 // it's not very rational behaviour..
36161                 
36162                 if(!this.wasLeaf && this.node.leaf){
36163                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36164                     delete this.c1;
36165                     delete this.c2;
36166                     this.wasLeaf = true;
36167                 }
36168             }
36169             var ecc = "x-tree-ec-icon "+cls;
36170             if(this.ecc != ecc){
36171                 this.ecNode.className = ecc;
36172                 this.ecc = ecc;
36173             }
36174         }
36175     },
36176
36177     getChildIndent : function(){
36178         if(!this.childIndent){
36179             var buf = [];
36180             var p = this.node;
36181             while(p){
36182                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36183                     if(!p.isLast()) {
36184                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36185                     } else {
36186                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36187                     }
36188                 }
36189                 p = p.parentNode;
36190             }
36191             this.childIndent = buf.join("");
36192         }
36193         return this.childIndent;
36194     },
36195
36196     renderIndent : function(){
36197         if(this.rendered){
36198             var indent = "";
36199             var p = this.node.parentNode;
36200             if(p){
36201                 indent = p.ui.getChildIndent();
36202             }
36203             if(this.indentMarkup != indent){ // don't rerender if not required
36204                 this.indentNode.innerHTML = indent;
36205                 this.indentMarkup = indent;
36206             }
36207             this.updateExpandIcon();
36208         }
36209     }
36210 };
36211
36212 Roo.tree.RootTreeNodeUI = function(){
36213     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36214 };
36215 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36216     render : function(){
36217         if(!this.rendered){
36218             var targetNode = this.node.ownerTree.innerCt.dom;
36219             this.node.expanded = true;
36220             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36221             this.wrap = this.ctNode = targetNode.firstChild;
36222         }
36223     },
36224     collapse : function(){
36225     },
36226     expand : function(){
36227     }
36228 });/*
36229  * Based on:
36230  * Ext JS Library 1.1.1
36231  * Copyright(c) 2006-2007, Ext JS, LLC.
36232  *
36233  * Originally Released Under LGPL - original licence link has changed is not relivant.
36234  *
36235  * Fork - LGPL
36236  * <script type="text/javascript">
36237  */
36238 /**
36239  * @class Roo.tree.TreeLoader
36240  * @extends Roo.util.Observable
36241  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36242  * nodes from a specified URL. The response must be a javascript Array definition
36243  * who's elements are node definition objects. eg:
36244  * <pre><code>
36245 {  success : true,
36246    data :      [
36247    
36248     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36249     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36250     ]
36251 }
36252
36253
36254 </code></pre>
36255  * <br><br>
36256  * The old style respose with just an array is still supported, but not recommended.
36257  * <br><br>
36258  *
36259  * A server request is sent, and child nodes are loaded only when a node is expanded.
36260  * The loading node's id is passed to the server under the parameter name "node" to
36261  * enable the server to produce the correct child nodes.
36262  * <br><br>
36263  * To pass extra parameters, an event handler may be attached to the "beforeload"
36264  * event, and the parameters specified in the TreeLoader's baseParams property:
36265  * <pre><code>
36266     myTreeLoader.on("beforeload", function(treeLoader, node) {
36267         this.baseParams.category = node.attributes.category;
36268     }, this);
36269     
36270 </code></pre>
36271  *
36272  * This would pass an HTTP parameter called "category" to the server containing
36273  * the value of the Node's "category" attribute.
36274  * @constructor
36275  * Creates a new Treeloader.
36276  * @param {Object} config A config object containing config properties.
36277  */
36278 Roo.tree.TreeLoader = function(config){
36279     this.baseParams = {};
36280     this.requestMethod = "POST";
36281     Roo.apply(this, config);
36282
36283     this.addEvents({
36284     
36285         /**
36286          * @event beforeload
36287          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36288          * @param {Object} This TreeLoader object.
36289          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36290          * @param {Object} callback The callback function specified in the {@link #load} call.
36291          */
36292         beforeload : true,
36293         /**
36294          * @event load
36295          * Fires when the node has been successfuly loaded.
36296          * @param {Object} This TreeLoader object.
36297          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36298          * @param {Object} response The response object containing the data from the server.
36299          */
36300         load : true,
36301         /**
36302          * @event loadexception
36303          * Fires if the network request failed.
36304          * @param {Object} This TreeLoader object.
36305          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36306          * @param {Object} response The response object containing the data from the server.
36307          */
36308         loadexception : true,
36309         /**
36310          * @event create
36311          * Fires before a node is created, enabling you to return custom Node types 
36312          * @param {Object} This TreeLoader object.
36313          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36314          */
36315         create : true
36316     });
36317
36318     Roo.tree.TreeLoader.superclass.constructor.call(this);
36319 };
36320
36321 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36322     /**
36323     * @cfg {String} dataUrl The URL from which to request a Json string which
36324     * specifies an array of node definition object representing the child nodes
36325     * to be loaded.
36326     */
36327     /**
36328     * @cfg {String} requestMethod either GET or POST
36329     * defaults to POST (due to BC)
36330     * to be loaded.
36331     */
36332     /**
36333     * @cfg {Object} baseParams (optional) An object containing properties which
36334     * specify HTTP parameters to be passed to each request for child nodes.
36335     */
36336     /**
36337     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36338     * created by this loader. If the attributes sent by the server have an attribute in this object,
36339     * they take priority.
36340     */
36341     /**
36342     * @cfg {Object} uiProviders (optional) An object containing properties which
36343     * 
36344     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36345     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36346     * <i>uiProvider</i> attribute of a returned child node is a string rather
36347     * than a reference to a TreeNodeUI implementation, this that string value
36348     * is used as a property name in the uiProviders object. You can define the provider named
36349     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36350     */
36351     uiProviders : {},
36352
36353     /**
36354     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36355     * child nodes before loading.
36356     */
36357     clearOnLoad : true,
36358
36359     /**
36360     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36361     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36362     * Grid query { data : [ .....] }
36363     */
36364     
36365     root : false,
36366      /**
36367     * @cfg {String} queryParam (optional) 
36368     * Name of the query as it will be passed on the querystring (defaults to 'node')
36369     * eg. the request will be ?node=[id]
36370     */
36371     
36372     
36373     queryParam: false,
36374     
36375     /**
36376      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36377      * This is called automatically when a node is expanded, but may be used to reload
36378      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36379      * @param {Roo.tree.TreeNode} node
36380      * @param {Function} callback
36381      */
36382     load : function(node, callback){
36383         if(this.clearOnLoad){
36384             while(node.firstChild){
36385                 node.removeChild(node.firstChild);
36386             }
36387         }
36388         if(node.attributes.children){ // preloaded json children
36389             var cs = node.attributes.children;
36390             for(var i = 0, len = cs.length; i < len; i++){
36391                 node.appendChild(this.createNode(cs[i]));
36392             }
36393             if(typeof callback == "function"){
36394                 callback();
36395             }
36396         }else if(this.dataUrl){
36397             this.requestData(node, callback);
36398         }
36399     },
36400
36401     getParams: function(node){
36402         var buf = [], bp = this.baseParams;
36403         for(var key in bp){
36404             if(typeof bp[key] != "function"){
36405                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36406             }
36407         }
36408         var n = this.queryParam === false ? 'node' : this.queryParam;
36409         buf.push(n + "=", encodeURIComponent(node.id));
36410         return buf.join("");
36411     },
36412
36413     requestData : function(node, callback){
36414         if(this.fireEvent("beforeload", this, node, callback) !== false){
36415             this.transId = Roo.Ajax.request({
36416                 method:this.requestMethod,
36417                 url: this.dataUrl||this.url,
36418                 success: this.handleResponse,
36419                 failure: this.handleFailure,
36420                 scope: this,
36421                 argument: {callback: callback, node: node},
36422                 params: this.getParams(node)
36423             });
36424         }else{
36425             // if the load is cancelled, make sure we notify
36426             // the node that we are done
36427             if(typeof callback == "function"){
36428                 callback();
36429             }
36430         }
36431     },
36432
36433     isLoading : function(){
36434         return this.transId ? true : false;
36435     },
36436
36437     abort : function(){
36438         if(this.isLoading()){
36439             Roo.Ajax.abort(this.transId);
36440         }
36441     },
36442
36443     // private
36444     createNode : function(attr)
36445     {
36446         // apply baseAttrs, nice idea Corey!
36447         if(this.baseAttrs){
36448             Roo.applyIf(attr, this.baseAttrs);
36449         }
36450         if(this.applyLoader !== false){
36451             attr.loader = this;
36452         }
36453         // uiProvider = depreciated..
36454         
36455         if(typeof(attr.uiProvider) == 'string'){
36456            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36457                 /**  eval:var:attr */ eval(attr.uiProvider);
36458         }
36459         if(typeof(this.uiProviders['default']) != 'undefined') {
36460             attr.uiProvider = this.uiProviders['default'];
36461         }
36462         
36463         this.fireEvent('create', this, attr);
36464         
36465         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36466         return(attr.leaf ?
36467                         new Roo.tree.TreeNode(attr) :
36468                         new Roo.tree.AsyncTreeNode(attr));
36469     },
36470
36471     processResponse : function(response, node, callback)
36472     {
36473         var json = response.responseText;
36474         try {
36475             
36476             var o = Roo.decode(json);
36477             
36478             if (this.root === false && typeof(o.success) != undefined) {
36479                 this.root = 'data'; // the default behaviour for list like data..
36480                 }
36481                 
36482             if (this.root !== false &&  !o.success) {
36483                 // it's a failure condition.
36484                 var a = response.argument;
36485                 this.fireEvent("loadexception", this, a.node, response);
36486                 Roo.log("Load failed - should have a handler really");
36487                 return;
36488             }
36489             
36490             
36491             
36492             if (this.root !== false) {
36493                  o = o[this.root];
36494             }
36495             
36496             for(var i = 0, len = o.length; i < len; i++){
36497                 var n = this.createNode(o[i]);
36498                 if(n){
36499                     node.appendChild(n);
36500                 }
36501             }
36502             if(typeof callback == "function"){
36503                 callback(this, node);
36504             }
36505         }catch(e){
36506             this.handleFailure(response);
36507         }
36508     },
36509
36510     handleResponse : function(response){
36511         this.transId = false;
36512         var a = response.argument;
36513         this.processResponse(response, a.node, a.callback);
36514         this.fireEvent("load", this, a.node, response);
36515     },
36516
36517     handleFailure : function(response)
36518     {
36519         // should handle failure better..
36520         this.transId = false;
36521         var a = response.argument;
36522         this.fireEvent("loadexception", this, a.node, response);
36523         if(typeof a.callback == "function"){
36524             a.callback(this, a.node);
36525         }
36526     }
36527 });/*
36528  * Based on:
36529  * Ext JS Library 1.1.1
36530  * Copyright(c) 2006-2007, Ext JS, LLC.
36531  *
36532  * Originally Released Under LGPL - original licence link has changed is not relivant.
36533  *
36534  * Fork - LGPL
36535  * <script type="text/javascript">
36536  */
36537
36538 /**
36539 * @class Roo.tree.TreeFilter
36540 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36541 * @param {TreePanel} tree
36542 * @param {Object} config (optional)
36543  */
36544 Roo.tree.TreeFilter = function(tree, config){
36545     this.tree = tree;
36546     this.filtered = {};
36547     Roo.apply(this, config);
36548 };
36549
36550 Roo.tree.TreeFilter.prototype = {
36551     clearBlank:false,
36552     reverse:false,
36553     autoClear:false,
36554     remove:false,
36555
36556      /**
36557      * Filter the data by a specific attribute.
36558      * @param {String/RegExp} value Either string that the attribute value
36559      * should start with or a RegExp to test against the attribute
36560      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36561      * @param {TreeNode} startNode (optional) The node to start the filter at.
36562      */
36563     filter : function(value, attr, startNode){
36564         attr = attr || "text";
36565         var f;
36566         if(typeof value == "string"){
36567             var vlen = value.length;
36568             // auto clear empty filter
36569             if(vlen == 0 && this.clearBlank){
36570                 this.clear();
36571                 return;
36572             }
36573             value = value.toLowerCase();
36574             f = function(n){
36575                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36576             };
36577         }else if(value.exec){ // regex?
36578             f = function(n){
36579                 return value.test(n.attributes[attr]);
36580             };
36581         }else{
36582             throw 'Illegal filter type, must be string or regex';
36583         }
36584         this.filterBy(f, null, startNode);
36585         },
36586
36587     /**
36588      * Filter by a function. The passed function will be called with each
36589      * node in the tree (or from the startNode). If the function returns true, the node is kept
36590      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36591      * @param {Function} fn The filter function
36592      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36593      */
36594     filterBy : function(fn, scope, startNode){
36595         startNode = startNode || this.tree.root;
36596         if(this.autoClear){
36597             this.clear();
36598         }
36599         var af = this.filtered, rv = this.reverse;
36600         var f = function(n){
36601             if(n == startNode){
36602                 return true;
36603             }
36604             if(af[n.id]){
36605                 return false;
36606             }
36607             var m = fn.call(scope || n, n);
36608             if(!m || rv){
36609                 af[n.id] = n;
36610                 n.ui.hide();
36611                 return false;
36612             }
36613             return true;
36614         };
36615         startNode.cascade(f);
36616         if(this.remove){
36617            for(var id in af){
36618                if(typeof id != "function"){
36619                    var n = af[id];
36620                    if(n && n.parentNode){
36621                        n.parentNode.removeChild(n);
36622                    }
36623                }
36624            }
36625         }
36626     },
36627
36628     /**
36629      * Clears the current filter. Note: with the "remove" option
36630      * set a filter cannot be cleared.
36631      */
36632     clear : function(){
36633         var t = this.tree;
36634         var af = this.filtered;
36635         for(var id in af){
36636             if(typeof id != "function"){
36637                 var n = af[id];
36638                 if(n){
36639                     n.ui.show();
36640                 }
36641             }
36642         }
36643         this.filtered = {};
36644     }
36645 };
36646 /*
36647  * Based on:
36648  * Ext JS Library 1.1.1
36649  * Copyright(c) 2006-2007, Ext JS, LLC.
36650  *
36651  * Originally Released Under LGPL - original licence link has changed is not relivant.
36652  *
36653  * Fork - LGPL
36654  * <script type="text/javascript">
36655  */
36656  
36657
36658 /**
36659  * @class Roo.tree.TreeSorter
36660  * Provides sorting of nodes in a TreePanel
36661  * 
36662  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36663  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36664  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36665  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36666  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36667  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36668  * @constructor
36669  * @param {TreePanel} tree
36670  * @param {Object} config
36671  */
36672 Roo.tree.TreeSorter = function(tree, config){
36673     Roo.apply(this, config);
36674     tree.on("beforechildrenrendered", this.doSort, this);
36675     tree.on("append", this.updateSort, this);
36676     tree.on("insert", this.updateSort, this);
36677     
36678     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36679     var p = this.property || "text";
36680     var sortType = this.sortType;
36681     var fs = this.folderSort;
36682     var cs = this.caseSensitive === true;
36683     var leafAttr = this.leafAttr || 'leaf';
36684
36685     this.sortFn = function(n1, n2){
36686         if(fs){
36687             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36688                 return 1;
36689             }
36690             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36691                 return -1;
36692             }
36693         }
36694         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36695         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36696         if(v1 < v2){
36697                         return dsc ? +1 : -1;
36698                 }else if(v1 > v2){
36699                         return dsc ? -1 : +1;
36700         }else{
36701                 return 0;
36702         }
36703     };
36704 };
36705
36706 Roo.tree.TreeSorter.prototype = {
36707     doSort : function(node){
36708         node.sort(this.sortFn);
36709     },
36710     
36711     compareNodes : function(n1, n2){
36712         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36713     },
36714     
36715     updateSort : function(tree, node){
36716         if(node.childrenRendered){
36717             this.doSort.defer(1, this, [node]);
36718         }
36719     }
36720 };/*
36721  * Based on:
36722  * Ext JS Library 1.1.1
36723  * Copyright(c) 2006-2007, Ext JS, LLC.
36724  *
36725  * Originally Released Under LGPL - original licence link has changed is not relivant.
36726  *
36727  * Fork - LGPL
36728  * <script type="text/javascript">
36729  */
36730
36731 if(Roo.dd.DropZone){
36732     
36733 Roo.tree.TreeDropZone = function(tree, config){
36734     this.allowParentInsert = false;
36735     this.allowContainerDrop = false;
36736     this.appendOnly = false;
36737     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36738     this.tree = tree;
36739     this.lastInsertClass = "x-tree-no-status";
36740     this.dragOverData = {};
36741 };
36742
36743 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36744     ddGroup : "TreeDD",
36745     scroll:  true,
36746     
36747     expandDelay : 1000,
36748     
36749     expandNode : function(node){
36750         if(node.hasChildNodes() && !node.isExpanded()){
36751             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36752         }
36753     },
36754     
36755     queueExpand : function(node){
36756         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36757     },
36758     
36759     cancelExpand : function(){
36760         if(this.expandProcId){
36761             clearTimeout(this.expandProcId);
36762             this.expandProcId = false;
36763         }
36764     },
36765     
36766     isValidDropPoint : function(n, pt, dd, e, data){
36767         if(!n || !data){ return false; }
36768         var targetNode = n.node;
36769         var dropNode = data.node;
36770         // default drop rules
36771         if(!(targetNode && targetNode.isTarget && pt)){
36772             return false;
36773         }
36774         if(pt == "append" && targetNode.allowChildren === false){
36775             return false;
36776         }
36777         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36778             return false;
36779         }
36780         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36781             return false;
36782         }
36783         // reuse the object
36784         var overEvent = this.dragOverData;
36785         overEvent.tree = this.tree;
36786         overEvent.target = targetNode;
36787         overEvent.data = data;
36788         overEvent.point = pt;
36789         overEvent.source = dd;
36790         overEvent.rawEvent = e;
36791         overEvent.dropNode = dropNode;
36792         overEvent.cancel = false;  
36793         var result = this.tree.fireEvent("nodedragover", overEvent);
36794         return overEvent.cancel === false && result !== false;
36795     },
36796     
36797     getDropPoint : function(e, n, dd)
36798     {
36799         var tn = n.node;
36800         if(tn.isRoot){
36801             return tn.allowChildren !== false ? "append" : false; // always append for root
36802         }
36803         var dragEl = n.ddel;
36804         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36805         var y = Roo.lib.Event.getPageY(e);
36806         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36807         
36808         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36809         var noAppend = tn.allowChildren === false;
36810         if(this.appendOnly || tn.parentNode.allowChildren === false){
36811             return noAppend ? false : "append";
36812         }
36813         var noBelow = false;
36814         if(!this.allowParentInsert){
36815             noBelow = tn.hasChildNodes() && tn.isExpanded();
36816         }
36817         var q = (b - t) / (noAppend ? 2 : 3);
36818         if(y >= t && y < (t + q)){
36819             return "above";
36820         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36821             return "below";
36822         }else{
36823             return "append";
36824         }
36825     },
36826     
36827     onNodeEnter : function(n, dd, e, data)
36828     {
36829         this.cancelExpand();
36830     },
36831     
36832     onNodeOver : function(n, dd, e, data)
36833     {
36834        
36835         var pt = this.getDropPoint(e, n, dd);
36836         var node = n.node;
36837         
36838         // auto node expand check
36839         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36840             this.queueExpand(node);
36841         }else if(pt != "append"){
36842             this.cancelExpand();
36843         }
36844         
36845         // set the insert point style on the target node
36846         var returnCls = this.dropNotAllowed;
36847         if(this.isValidDropPoint(n, pt, dd, e, data)){
36848            if(pt){
36849                var el = n.ddel;
36850                var cls;
36851                if(pt == "above"){
36852                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36853                    cls = "x-tree-drag-insert-above";
36854                }else if(pt == "below"){
36855                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36856                    cls = "x-tree-drag-insert-below";
36857                }else{
36858                    returnCls = "x-tree-drop-ok-append";
36859                    cls = "x-tree-drag-append";
36860                }
36861                if(this.lastInsertClass != cls){
36862                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36863                    this.lastInsertClass = cls;
36864                }
36865            }
36866        }
36867        return returnCls;
36868     },
36869     
36870     onNodeOut : function(n, dd, e, data){
36871         
36872         this.cancelExpand();
36873         this.removeDropIndicators(n);
36874     },
36875     
36876     onNodeDrop : function(n, dd, e, data){
36877         var point = this.getDropPoint(e, n, dd);
36878         var targetNode = n.node;
36879         targetNode.ui.startDrop();
36880         if(!this.isValidDropPoint(n, point, dd, e, data)){
36881             targetNode.ui.endDrop();
36882             return false;
36883         }
36884         // first try to find the drop node
36885         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36886         var dropEvent = {
36887             tree : this.tree,
36888             target: targetNode,
36889             data: data,
36890             point: point,
36891             source: dd,
36892             rawEvent: e,
36893             dropNode: dropNode,
36894             cancel: !dropNode   
36895         };
36896         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36897         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36898             targetNode.ui.endDrop();
36899             return false;
36900         }
36901         // allow target changing
36902         targetNode = dropEvent.target;
36903         if(point == "append" && !targetNode.isExpanded()){
36904             targetNode.expand(false, null, function(){
36905                 this.completeDrop(dropEvent);
36906             }.createDelegate(this));
36907         }else{
36908             this.completeDrop(dropEvent);
36909         }
36910         return true;
36911     },
36912     
36913     completeDrop : function(de){
36914         var ns = de.dropNode, p = de.point, t = de.target;
36915         if(!(ns instanceof Array)){
36916             ns = [ns];
36917         }
36918         var n;
36919         for(var i = 0, len = ns.length; i < len; i++){
36920             n = ns[i];
36921             if(p == "above"){
36922                 t.parentNode.insertBefore(n, t);
36923             }else if(p == "below"){
36924                 t.parentNode.insertBefore(n, t.nextSibling);
36925             }else{
36926                 t.appendChild(n);
36927             }
36928         }
36929         n.ui.focus();
36930         if(this.tree.hlDrop){
36931             n.ui.highlight();
36932         }
36933         t.ui.endDrop();
36934         this.tree.fireEvent("nodedrop", de);
36935     },
36936     
36937     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36938         if(this.tree.hlDrop){
36939             dropNode.ui.focus();
36940             dropNode.ui.highlight();
36941         }
36942         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
36943     },
36944     
36945     getTree : function(){
36946         return this.tree;
36947     },
36948     
36949     removeDropIndicators : function(n){
36950         if(n && n.ddel){
36951             var el = n.ddel;
36952             Roo.fly(el).removeClass([
36953                     "x-tree-drag-insert-above",
36954                     "x-tree-drag-insert-below",
36955                     "x-tree-drag-append"]);
36956             this.lastInsertClass = "_noclass";
36957         }
36958     },
36959     
36960     beforeDragDrop : function(target, e, id){
36961         this.cancelExpand();
36962         return true;
36963     },
36964     
36965     afterRepair : function(data){
36966         if(data && Roo.enableFx){
36967             data.node.ui.highlight();
36968         }
36969         this.hideProxy();
36970     } 
36971     
36972 });
36973
36974 }
36975 /*
36976  * Based on:
36977  * Ext JS Library 1.1.1
36978  * Copyright(c) 2006-2007, Ext JS, LLC.
36979  *
36980  * Originally Released Under LGPL - original licence link has changed is not relivant.
36981  *
36982  * Fork - LGPL
36983  * <script type="text/javascript">
36984  */
36985  
36986
36987 if(Roo.dd.DragZone){
36988 Roo.tree.TreeDragZone = function(tree, config){
36989     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
36990     this.tree = tree;
36991 };
36992
36993 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
36994     ddGroup : "TreeDD",
36995    
36996     onBeforeDrag : function(data, e){
36997         var n = data.node;
36998         return n && n.draggable && !n.disabled;
36999     },
37000      
37001     
37002     onInitDrag : function(e){
37003         var data = this.dragData;
37004         this.tree.getSelectionModel().select(data.node);
37005         this.proxy.update("");
37006         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37007         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37008     },
37009     
37010     getRepairXY : function(e, data){
37011         return data.node.ui.getDDRepairXY();
37012     },
37013     
37014     onEndDrag : function(data, e){
37015         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37016         
37017         
37018     },
37019     
37020     onValidDrop : function(dd, e, id){
37021         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37022         this.hideProxy();
37023     },
37024     
37025     beforeInvalidDrop : function(e, id){
37026         // this scrolls the original position back into view
37027         var sm = this.tree.getSelectionModel();
37028         sm.clearSelections();
37029         sm.select(this.dragData.node);
37030     }
37031 });
37032 }/*
37033  * Based on:
37034  * Ext JS Library 1.1.1
37035  * Copyright(c) 2006-2007, Ext JS, LLC.
37036  *
37037  * Originally Released Under LGPL - original licence link has changed is not relivant.
37038  *
37039  * Fork - LGPL
37040  * <script type="text/javascript">
37041  */
37042 /**
37043  * @class Roo.tree.TreeEditor
37044  * @extends Roo.Editor
37045  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37046  * as the editor field.
37047  * @constructor
37048  * @param {Object} config (used to be the tree panel.)
37049  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37050  * 
37051  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37052  * @cfg {Roo.form.TextField|Object} field The field configuration
37053  *
37054  * 
37055  */
37056 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37057     var tree = config;
37058     var field;
37059     if (oldconfig) { // old style..
37060         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37061     } else {
37062         // new style..
37063         tree = config.tree;
37064         config.field = config.field  || {};
37065         config.field.xtype = 'TextField';
37066         field = Roo.factory(config.field, Roo.form);
37067     }
37068     config = config || {};
37069     
37070     
37071     this.addEvents({
37072         /**
37073          * @event beforenodeedit
37074          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37075          * false from the handler of this event.
37076          * @param {Editor} this
37077          * @param {Roo.tree.Node} node 
37078          */
37079         "beforenodeedit" : true
37080     });
37081     
37082     //Roo.log(config);
37083     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37084
37085     this.tree = tree;
37086
37087     tree.on('beforeclick', this.beforeNodeClick, this);
37088     tree.getTreeEl().on('mousedown', this.hide, this);
37089     this.on('complete', this.updateNode, this);
37090     this.on('beforestartedit', this.fitToTree, this);
37091     this.on('startedit', this.bindScroll, this, {delay:10});
37092     this.on('specialkey', this.onSpecialKey, this);
37093 };
37094
37095 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37096     /**
37097      * @cfg {String} alignment
37098      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37099      */
37100     alignment: "l-l",
37101     // inherit
37102     autoSize: false,
37103     /**
37104      * @cfg {Boolean} hideEl
37105      * True to hide the bound element while the editor is displayed (defaults to false)
37106      */
37107     hideEl : false,
37108     /**
37109      * @cfg {String} cls
37110      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37111      */
37112     cls: "x-small-editor x-tree-editor",
37113     /**
37114      * @cfg {Boolean} shim
37115      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37116      */
37117     shim:false,
37118     // inherit
37119     shadow:"frame",
37120     /**
37121      * @cfg {Number} maxWidth
37122      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37123      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37124      * scroll and client offsets into account prior to each edit.
37125      */
37126     maxWidth: 250,
37127
37128     editDelay : 350,
37129
37130     // private
37131     fitToTree : function(ed, el){
37132         var td = this.tree.getTreeEl().dom, nd = el.dom;
37133         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37134             td.scrollLeft = nd.offsetLeft;
37135         }
37136         var w = Math.min(
37137                 this.maxWidth,
37138                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37139         this.setSize(w, '');
37140         
37141         return this.fireEvent('beforenodeedit', this, this.editNode);
37142         
37143     },
37144
37145     // private
37146     triggerEdit : function(node){
37147         this.completeEdit();
37148         this.editNode = node;
37149         this.startEdit(node.ui.textNode, node.text);
37150     },
37151
37152     // private
37153     bindScroll : function(){
37154         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37155     },
37156
37157     // private
37158     beforeNodeClick : function(node, e){
37159         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37160         this.lastClick = new Date();
37161         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37162             e.stopEvent();
37163             this.triggerEdit(node);
37164             return false;
37165         }
37166         return true;
37167     },
37168
37169     // private
37170     updateNode : function(ed, value){
37171         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37172         this.editNode.setText(value);
37173     },
37174
37175     // private
37176     onHide : function(){
37177         Roo.tree.TreeEditor.superclass.onHide.call(this);
37178         if(this.editNode){
37179             this.editNode.ui.focus();
37180         }
37181     },
37182
37183     // private
37184     onSpecialKey : function(field, e){
37185         var k = e.getKey();
37186         if(k == e.ESC){
37187             e.stopEvent();
37188             this.cancelEdit();
37189         }else if(k == e.ENTER && !e.hasModifier()){
37190             e.stopEvent();
37191             this.completeEdit();
37192         }
37193     }
37194 });//<Script type="text/javascript">
37195 /*
37196  * Based on:
37197  * Ext JS Library 1.1.1
37198  * Copyright(c) 2006-2007, Ext JS, LLC.
37199  *
37200  * Originally Released Under LGPL - original licence link has changed is not relivant.
37201  *
37202  * Fork - LGPL
37203  * <script type="text/javascript">
37204  */
37205  
37206 /**
37207  * Not documented??? - probably should be...
37208  */
37209
37210 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37211     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37212     
37213     renderElements : function(n, a, targetNode, bulkRender){
37214         //consel.log("renderElements?");
37215         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37216
37217         var t = n.getOwnerTree();
37218         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37219         
37220         var cols = t.columns;
37221         var bw = t.borderWidth;
37222         var c = cols[0];
37223         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37224          var cb = typeof a.checked == "boolean";
37225         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37226         var colcls = 'x-t-' + tid + '-c0';
37227         var buf = [
37228             '<li class="x-tree-node">',
37229             
37230                 
37231                 '<div class="x-tree-node-el ', a.cls,'">',
37232                     // extran...
37233                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37234                 
37235                 
37236                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37237                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37238                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37239                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37240                            (a.iconCls ? ' '+a.iconCls : ''),
37241                            '" unselectable="on" />',
37242                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37243                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37244                              
37245                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37246                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37247                             '<span unselectable="on" qtip="' + tx + '">',
37248                              tx,
37249                              '</span></a>' ,
37250                     '</div>',
37251                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37252                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37253                  ];
37254         for(var i = 1, len = cols.length; i < len; i++){
37255             c = cols[i];
37256             colcls = 'x-t-' + tid + '-c' +i;
37257             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37258             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37259                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37260                       "</div>");
37261          }
37262          
37263          buf.push(
37264             '</a>',
37265             '<div class="x-clear"></div></div>',
37266             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37267             "</li>");
37268         
37269         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37270             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37271                                 n.nextSibling.ui.getEl(), buf.join(""));
37272         }else{
37273             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37274         }
37275         var el = this.wrap.firstChild;
37276         this.elRow = el;
37277         this.elNode = el.firstChild;
37278         this.ranchor = el.childNodes[1];
37279         this.ctNode = this.wrap.childNodes[1];
37280         var cs = el.firstChild.childNodes;
37281         this.indentNode = cs[0];
37282         this.ecNode = cs[1];
37283         this.iconNode = cs[2];
37284         var index = 3;
37285         if(cb){
37286             this.checkbox = cs[3];
37287             index++;
37288         }
37289         this.anchor = cs[index];
37290         
37291         this.textNode = cs[index].firstChild;
37292         
37293         //el.on("click", this.onClick, this);
37294         //el.on("dblclick", this.onDblClick, this);
37295         
37296         
37297        // console.log(this);
37298     },
37299     initEvents : function(){
37300         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37301         
37302             
37303         var a = this.ranchor;
37304
37305         var el = Roo.get(a);
37306
37307         if(Roo.isOpera){ // opera render bug ignores the CSS
37308             el.setStyle("text-decoration", "none");
37309         }
37310
37311         el.on("click", this.onClick, this);
37312         el.on("dblclick", this.onDblClick, this);
37313         el.on("contextmenu", this.onContextMenu, this);
37314         
37315     },
37316     
37317     /*onSelectedChange : function(state){
37318         if(state){
37319             this.focus();
37320             this.addClass("x-tree-selected");
37321         }else{
37322             //this.blur();
37323             this.removeClass("x-tree-selected");
37324         }
37325     },*/
37326     addClass : function(cls){
37327         if(this.elRow){
37328             Roo.fly(this.elRow).addClass(cls);
37329         }
37330         
37331     },
37332     
37333     
37334     removeClass : function(cls){
37335         if(this.elRow){
37336             Roo.fly(this.elRow).removeClass(cls);
37337         }
37338     }
37339
37340     
37341     
37342 });//<Script type="text/javascript">
37343
37344 /*
37345  * Based on:
37346  * Ext JS Library 1.1.1
37347  * Copyright(c) 2006-2007, Ext JS, LLC.
37348  *
37349  * Originally Released Under LGPL - original licence link has changed is not relivant.
37350  *
37351  * Fork - LGPL
37352  * <script type="text/javascript">
37353  */
37354  
37355
37356 /**
37357  * @class Roo.tree.ColumnTree
37358  * @extends Roo.data.TreePanel
37359  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37360  * @cfg {int} borderWidth  compined right/left border allowance
37361  * @constructor
37362  * @param {String/HTMLElement/Element} el The container element
37363  * @param {Object} config
37364  */
37365 Roo.tree.ColumnTree =  function(el, config)
37366 {
37367    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37368    this.addEvents({
37369         /**
37370         * @event resize
37371         * Fire this event on a container when it resizes
37372         * @param {int} w Width
37373         * @param {int} h Height
37374         */
37375        "resize" : true
37376     });
37377     this.on('resize', this.onResize, this);
37378 };
37379
37380 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37381     //lines:false,
37382     
37383     
37384     borderWidth: Roo.isBorderBox ? 0 : 2, 
37385     headEls : false,
37386     
37387     render : function(){
37388         // add the header.....
37389        
37390         Roo.tree.ColumnTree.superclass.render.apply(this);
37391         
37392         this.el.addClass('x-column-tree');
37393         
37394         this.headers = this.el.createChild(
37395             {cls:'x-tree-headers'},this.innerCt.dom);
37396    
37397         var cols = this.columns, c;
37398         var totalWidth = 0;
37399         this.headEls = [];
37400         var  len = cols.length;
37401         for(var i = 0; i < len; i++){
37402              c = cols[i];
37403              totalWidth += c.width;
37404             this.headEls.push(this.headers.createChild({
37405                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37406                  cn: {
37407                      cls:'x-tree-hd-text',
37408                      html: c.header
37409                  },
37410                  style:'width:'+(c.width-this.borderWidth)+'px;'
37411              }));
37412         }
37413         this.headers.createChild({cls:'x-clear'});
37414         // prevent floats from wrapping when clipped
37415         this.headers.setWidth(totalWidth);
37416         //this.innerCt.setWidth(totalWidth);
37417         this.innerCt.setStyle({ overflow: 'auto' });
37418         this.onResize(this.width, this.height);
37419              
37420         
37421     },
37422     onResize : function(w,h)
37423     {
37424         this.height = h;
37425         this.width = w;
37426         // resize cols..
37427         this.innerCt.setWidth(this.width);
37428         this.innerCt.setHeight(this.height-20);
37429         
37430         // headers...
37431         var cols = this.columns, c;
37432         var totalWidth = 0;
37433         var expEl = false;
37434         var len = cols.length;
37435         for(var i = 0; i < len; i++){
37436             c = cols[i];
37437             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37438                 // it's the expander..
37439                 expEl  = this.headEls[i];
37440                 continue;
37441             }
37442             totalWidth += c.width;
37443             
37444         }
37445         if (expEl) {
37446             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37447         }
37448         this.headers.setWidth(w-20);
37449
37450         
37451         
37452         
37453     }
37454 });
37455 /*
37456  * Based on:
37457  * Ext JS Library 1.1.1
37458  * Copyright(c) 2006-2007, Ext JS, LLC.
37459  *
37460  * Originally Released Under LGPL - original licence link has changed is not relivant.
37461  *
37462  * Fork - LGPL
37463  * <script type="text/javascript">
37464  */
37465  
37466 /**
37467  * @class Roo.menu.Menu
37468  * @extends Roo.util.Observable
37469  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37470  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37471  * @constructor
37472  * Creates a new Menu
37473  * @param {Object} config Configuration options
37474  */
37475 Roo.menu.Menu = function(config){
37476     
37477     Roo.menu.Menu.superclass.constructor.call(this, config);
37478     
37479     this.id = this.id || Roo.id();
37480     this.addEvents({
37481         /**
37482          * @event beforeshow
37483          * Fires before this menu is displayed
37484          * @param {Roo.menu.Menu} this
37485          */
37486         beforeshow : true,
37487         /**
37488          * @event beforehide
37489          * Fires before this menu is hidden
37490          * @param {Roo.menu.Menu} this
37491          */
37492         beforehide : true,
37493         /**
37494          * @event show
37495          * Fires after this menu is displayed
37496          * @param {Roo.menu.Menu} this
37497          */
37498         show : true,
37499         /**
37500          * @event hide
37501          * Fires after this menu is hidden
37502          * @param {Roo.menu.Menu} this
37503          */
37504         hide : true,
37505         /**
37506          * @event click
37507          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37508          * @param {Roo.menu.Menu} this
37509          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37510          * @param {Roo.EventObject} e
37511          */
37512         click : true,
37513         /**
37514          * @event mouseover
37515          * Fires when the mouse is hovering over this menu
37516          * @param {Roo.menu.Menu} this
37517          * @param {Roo.EventObject} e
37518          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37519          */
37520         mouseover : true,
37521         /**
37522          * @event mouseout
37523          * Fires when the mouse exits this menu
37524          * @param {Roo.menu.Menu} this
37525          * @param {Roo.EventObject} e
37526          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37527          */
37528         mouseout : true,
37529         /**
37530          * @event itemclick
37531          * Fires when a menu item contained in this menu is clicked
37532          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37533          * @param {Roo.EventObject} e
37534          */
37535         itemclick: true
37536     });
37537     if (this.registerMenu) {
37538         Roo.menu.MenuMgr.register(this);
37539     }
37540     
37541     var mis = this.items;
37542     this.items = new Roo.util.MixedCollection();
37543     if(mis){
37544         this.add.apply(this, mis);
37545     }
37546 };
37547
37548 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37549     /**
37550      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37551      */
37552     minWidth : 120,
37553     /**
37554      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37555      * for bottom-right shadow (defaults to "sides")
37556      */
37557     shadow : "sides",
37558     /**
37559      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37560      * this menu (defaults to "tl-tr?")
37561      */
37562     subMenuAlign : "tl-tr?",
37563     /**
37564      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37565      * relative to its element of origin (defaults to "tl-bl?")
37566      */
37567     defaultAlign : "tl-bl?",
37568     /**
37569      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37570      */
37571     allowOtherMenus : false,
37572     /**
37573      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37574      */
37575     registerMenu : true,
37576
37577     hidden:true,
37578
37579     // private
37580     render : function(){
37581         if(this.el){
37582             return;
37583         }
37584         var el = this.el = new Roo.Layer({
37585             cls: "x-menu",
37586             shadow:this.shadow,
37587             constrain: false,
37588             parentEl: this.parentEl || document.body,
37589             zindex:15000
37590         });
37591
37592         this.keyNav = new Roo.menu.MenuNav(this);
37593
37594         if(this.plain){
37595             el.addClass("x-menu-plain");
37596         }
37597         if(this.cls){
37598             el.addClass(this.cls);
37599         }
37600         // generic focus element
37601         this.focusEl = el.createChild({
37602             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37603         });
37604         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37605         //disabling touch- as it's causing issues ..
37606         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37607         ul.on('click'   , this.onClick, this);
37608         
37609         
37610         ul.on("mouseover", this.onMouseOver, this);
37611         ul.on("mouseout", this.onMouseOut, this);
37612         this.items.each(function(item){
37613             if (item.hidden) {
37614                 return;
37615             }
37616             
37617             var li = document.createElement("li");
37618             li.className = "x-menu-list-item";
37619             ul.dom.appendChild(li);
37620             item.render(li, this);
37621         }, this);
37622         this.ul = ul;
37623         this.autoWidth();
37624     },
37625
37626     // private
37627     autoWidth : function(){
37628         var el = this.el, ul = this.ul;
37629         if(!el){
37630             return;
37631         }
37632         var w = this.width;
37633         if(w){
37634             el.setWidth(w);
37635         }else if(Roo.isIE){
37636             el.setWidth(this.minWidth);
37637             var t = el.dom.offsetWidth; // force recalc
37638             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37639         }
37640     },
37641
37642     // private
37643     delayAutoWidth : function(){
37644         if(this.rendered){
37645             if(!this.awTask){
37646                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37647             }
37648             this.awTask.delay(20);
37649         }
37650     },
37651
37652     // private
37653     findTargetItem : function(e){
37654         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37655         if(t && t.menuItemId){
37656             return this.items.get(t.menuItemId);
37657         }
37658     },
37659
37660     // private
37661     onClick : function(e){
37662         Roo.log("menu.onClick");
37663         var t = this.findTargetItem(e);
37664         if(!t){
37665             return;
37666         }
37667         Roo.log(e);
37668         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37669             if(t == this.activeItem && t.shouldDeactivate(e)){
37670                 this.activeItem.deactivate();
37671                 delete this.activeItem;
37672                 return;
37673             }
37674             if(t.canActivate){
37675                 this.setActiveItem(t, true);
37676             }
37677             return;
37678             
37679             
37680         }
37681         
37682         t.onClick(e);
37683         this.fireEvent("click", this, t, e);
37684     },
37685
37686     // private
37687     setActiveItem : function(item, autoExpand){
37688         if(item != this.activeItem){
37689             if(this.activeItem){
37690                 this.activeItem.deactivate();
37691             }
37692             this.activeItem = item;
37693             item.activate(autoExpand);
37694         }else if(autoExpand){
37695             item.expandMenu();
37696         }
37697     },
37698
37699     // private
37700     tryActivate : function(start, step){
37701         var items = this.items;
37702         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37703             var item = items.get(i);
37704             if(!item.disabled && item.canActivate){
37705                 this.setActiveItem(item, false);
37706                 return item;
37707             }
37708         }
37709         return false;
37710     },
37711
37712     // private
37713     onMouseOver : function(e){
37714         var t;
37715         if(t = this.findTargetItem(e)){
37716             if(t.canActivate && !t.disabled){
37717                 this.setActiveItem(t, true);
37718             }
37719         }
37720         this.fireEvent("mouseover", this, e, t);
37721     },
37722
37723     // private
37724     onMouseOut : function(e){
37725         var t;
37726         if(t = this.findTargetItem(e)){
37727             if(t == this.activeItem && t.shouldDeactivate(e)){
37728                 this.activeItem.deactivate();
37729                 delete this.activeItem;
37730             }
37731         }
37732         this.fireEvent("mouseout", this, e, t);
37733     },
37734
37735     /**
37736      * Read-only.  Returns true if the menu is currently displayed, else false.
37737      * @type Boolean
37738      */
37739     isVisible : function(){
37740         return this.el && !this.hidden;
37741     },
37742
37743     /**
37744      * Displays this menu relative to another element
37745      * @param {String/HTMLElement/Roo.Element} element The element to align to
37746      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37747      * the element (defaults to this.defaultAlign)
37748      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37749      */
37750     show : function(el, pos, parentMenu){
37751         this.parentMenu = parentMenu;
37752         if(!this.el){
37753             this.render();
37754         }
37755         this.fireEvent("beforeshow", this);
37756         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37757     },
37758
37759     /**
37760      * Displays this menu at a specific xy position
37761      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37762      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37763      */
37764     showAt : function(xy, parentMenu, /* private: */_e){
37765         this.parentMenu = parentMenu;
37766         if(!this.el){
37767             this.render();
37768         }
37769         if(_e !== false){
37770             this.fireEvent("beforeshow", this);
37771             xy = this.el.adjustForConstraints(xy);
37772         }
37773         this.el.setXY(xy);
37774         this.el.show();
37775         this.hidden = false;
37776         this.focus();
37777         this.fireEvent("show", this);
37778     },
37779
37780     focus : function(){
37781         if(!this.hidden){
37782             this.doFocus.defer(50, this);
37783         }
37784     },
37785
37786     doFocus : function(){
37787         if(!this.hidden){
37788             this.focusEl.focus();
37789         }
37790     },
37791
37792     /**
37793      * Hides this menu and optionally all parent menus
37794      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37795      */
37796     hide : function(deep){
37797         if(this.el && this.isVisible()){
37798             this.fireEvent("beforehide", this);
37799             if(this.activeItem){
37800                 this.activeItem.deactivate();
37801                 this.activeItem = null;
37802             }
37803             this.el.hide();
37804             this.hidden = true;
37805             this.fireEvent("hide", this);
37806         }
37807         if(deep === true && this.parentMenu){
37808             this.parentMenu.hide(true);
37809         }
37810     },
37811
37812     /**
37813      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37814      * Any of the following are valid:
37815      * <ul>
37816      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37817      * <li>An HTMLElement object which will be converted to a menu item</li>
37818      * <li>A menu item config object that will be created as a new menu item</li>
37819      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37820      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37821      * </ul>
37822      * Usage:
37823      * <pre><code>
37824 // Create the menu
37825 var menu = new Roo.menu.Menu();
37826
37827 // Create a menu item to add by reference
37828 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37829
37830 // Add a bunch of items at once using different methods.
37831 // Only the last item added will be returned.
37832 var item = menu.add(
37833     menuItem,                // add existing item by ref
37834     'Dynamic Item',          // new TextItem
37835     '-',                     // new separator
37836     { text: 'Config Item' }  // new item by config
37837 );
37838 </code></pre>
37839      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37840      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37841      */
37842     add : function(){
37843         var a = arguments, l = a.length, item;
37844         for(var i = 0; i < l; i++){
37845             var el = a[i];
37846             if ((typeof(el) == "object") && el.xtype && el.xns) {
37847                 el = Roo.factory(el, Roo.menu);
37848             }
37849             
37850             if(el.render){ // some kind of Item
37851                 item = this.addItem(el);
37852             }else if(typeof el == "string"){ // string
37853                 if(el == "separator" || el == "-"){
37854                     item = this.addSeparator();
37855                 }else{
37856                     item = this.addText(el);
37857                 }
37858             }else if(el.tagName || el.el){ // element
37859                 item = this.addElement(el);
37860             }else if(typeof el == "object"){ // must be menu item config?
37861                 item = this.addMenuItem(el);
37862             }
37863         }
37864         return item;
37865     },
37866
37867     /**
37868      * Returns this menu's underlying {@link Roo.Element} object
37869      * @return {Roo.Element} The element
37870      */
37871     getEl : function(){
37872         if(!this.el){
37873             this.render();
37874         }
37875         return this.el;
37876     },
37877
37878     /**
37879      * Adds a separator bar to the menu
37880      * @return {Roo.menu.Item} The menu item that was added
37881      */
37882     addSeparator : function(){
37883         return this.addItem(new Roo.menu.Separator());
37884     },
37885
37886     /**
37887      * Adds an {@link Roo.Element} object to the menu
37888      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37889      * @return {Roo.menu.Item} The menu item that was added
37890      */
37891     addElement : function(el){
37892         return this.addItem(new Roo.menu.BaseItem(el));
37893     },
37894
37895     /**
37896      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37897      * @param {Roo.menu.Item} item The menu item to add
37898      * @return {Roo.menu.Item} The menu item that was added
37899      */
37900     addItem : function(item){
37901         this.items.add(item);
37902         if(this.ul){
37903             var li = document.createElement("li");
37904             li.className = "x-menu-list-item";
37905             this.ul.dom.appendChild(li);
37906             item.render(li, this);
37907             this.delayAutoWidth();
37908         }
37909         return item;
37910     },
37911
37912     /**
37913      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37914      * @param {Object} config A MenuItem config object
37915      * @return {Roo.menu.Item} The menu item that was added
37916      */
37917     addMenuItem : function(config){
37918         if(!(config instanceof Roo.menu.Item)){
37919             if(typeof config.checked == "boolean"){ // must be check menu item config?
37920                 config = new Roo.menu.CheckItem(config);
37921             }else{
37922                 config = new Roo.menu.Item(config);
37923             }
37924         }
37925         return this.addItem(config);
37926     },
37927
37928     /**
37929      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37930      * @param {String} text The text to display in the menu item
37931      * @return {Roo.menu.Item} The menu item that was added
37932      */
37933     addText : function(text){
37934         return this.addItem(new Roo.menu.TextItem({ text : text }));
37935     },
37936
37937     /**
37938      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37939      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
37940      * @param {Roo.menu.Item} item The menu item to add
37941      * @return {Roo.menu.Item} The menu item that was added
37942      */
37943     insert : function(index, item){
37944         this.items.insert(index, item);
37945         if(this.ul){
37946             var li = document.createElement("li");
37947             li.className = "x-menu-list-item";
37948             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
37949             item.render(li, this);
37950             this.delayAutoWidth();
37951         }
37952         return item;
37953     },
37954
37955     /**
37956      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
37957      * @param {Roo.menu.Item} item The menu item to remove
37958      */
37959     remove : function(item){
37960         this.items.removeKey(item.id);
37961         item.destroy();
37962     },
37963
37964     /**
37965      * Removes and destroys all items in the menu
37966      */
37967     removeAll : function(){
37968         var f;
37969         while(f = this.items.first()){
37970             this.remove(f);
37971         }
37972     }
37973 });
37974
37975 // MenuNav is a private utility class used internally by the Menu
37976 Roo.menu.MenuNav = function(menu){
37977     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
37978     this.scope = this.menu = menu;
37979 };
37980
37981 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
37982     doRelay : function(e, h){
37983         var k = e.getKey();
37984         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
37985             this.menu.tryActivate(0, 1);
37986             return false;
37987         }
37988         return h.call(this.scope || this, e, this.menu);
37989     },
37990
37991     up : function(e, m){
37992         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
37993             m.tryActivate(m.items.length-1, -1);
37994         }
37995     },
37996
37997     down : function(e, m){
37998         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
37999             m.tryActivate(0, 1);
38000         }
38001     },
38002
38003     right : function(e, m){
38004         if(m.activeItem){
38005             m.activeItem.expandMenu(true);
38006         }
38007     },
38008
38009     left : function(e, m){
38010         m.hide();
38011         if(m.parentMenu && m.parentMenu.activeItem){
38012             m.parentMenu.activeItem.activate();
38013         }
38014     },
38015
38016     enter : function(e, m){
38017         if(m.activeItem){
38018             e.stopPropagation();
38019             m.activeItem.onClick(e);
38020             m.fireEvent("click", this, m.activeItem);
38021             return true;
38022         }
38023     }
38024 });/*
38025  * Based on:
38026  * Ext JS Library 1.1.1
38027  * Copyright(c) 2006-2007, Ext JS, LLC.
38028  *
38029  * Originally Released Under LGPL - original licence link has changed is not relivant.
38030  *
38031  * Fork - LGPL
38032  * <script type="text/javascript">
38033  */
38034  
38035 /**
38036  * @class Roo.menu.MenuMgr
38037  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38038  * @singleton
38039  */
38040 Roo.menu.MenuMgr = function(){
38041    var menus, active, groups = {}, attached = false, lastShow = new Date();
38042
38043    // private - called when first menu is created
38044    function init(){
38045        menus = {};
38046        active = new Roo.util.MixedCollection();
38047        Roo.get(document).addKeyListener(27, function(){
38048            if(active.length > 0){
38049                hideAll();
38050            }
38051        });
38052    }
38053
38054    // private
38055    function hideAll(){
38056        if(active && active.length > 0){
38057            var c = active.clone();
38058            c.each(function(m){
38059                m.hide();
38060            });
38061        }
38062    }
38063
38064    // private
38065    function onHide(m){
38066        active.remove(m);
38067        if(active.length < 1){
38068            Roo.get(document).un("mousedown", onMouseDown);
38069            attached = false;
38070        }
38071    }
38072
38073    // private
38074    function onShow(m){
38075        var last = active.last();
38076        lastShow = new Date();
38077        active.add(m);
38078        if(!attached){
38079            Roo.get(document).on("mousedown", onMouseDown);
38080            attached = true;
38081        }
38082        if(m.parentMenu){
38083           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38084           m.parentMenu.activeChild = m;
38085        }else if(last && last.isVisible()){
38086           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38087        }
38088    }
38089
38090    // private
38091    function onBeforeHide(m){
38092        if(m.activeChild){
38093            m.activeChild.hide();
38094        }
38095        if(m.autoHideTimer){
38096            clearTimeout(m.autoHideTimer);
38097            delete m.autoHideTimer;
38098        }
38099    }
38100
38101    // private
38102    function onBeforeShow(m){
38103        var pm = m.parentMenu;
38104        if(!pm && !m.allowOtherMenus){
38105            hideAll();
38106        }else if(pm && pm.activeChild && active != m){
38107            pm.activeChild.hide();
38108        }
38109    }
38110
38111    // private
38112    function onMouseDown(e){
38113        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38114            hideAll();
38115        }
38116    }
38117
38118    // private
38119    function onBeforeCheck(mi, state){
38120        if(state){
38121            var g = groups[mi.group];
38122            for(var i = 0, l = g.length; i < l; i++){
38123                if(g[i] != mi){
38124                    g[i].setChecked(false);
38125                }
38126            }
38127        }
38128    }
38129
38130    return {
38131
38132        /**
38133         * Hides all menus that are currently visible
38134         */
38135        hideAll : function(){
38136             hideAll();  
38137        },
38138
38139        // private
38140        register : function(menu){
38141            if(!menus){
38142                init();
38143            }
38144            menus[menu.id] = menu;
38145            menu.on("beforehide", onBeforeHide);
38146            menu.on("hide", onHide);
38147            menu.on("beforeshow", onBeforeShow);
38148            menu.on("show", onShow);
38149            var g = menu.group;
38150            if(g && menu.events["checkchange"]){
38151                if(!groups[g]){
38152                    groups[g] = [];
38153                }
38154                groups[g].push(menu);
38155                menu.on("checkchange", onCheck);
38156            }
38157        },
38158
38159         /**
38160          * Returns a {@link Roo.menu.Menu} object
38161          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38162          * be used to generate and return a new Menu instance.
38163          */
38164        get : function(menu){
38165            if(typeof menu == "string"){ // menu id
38166                return menus[menu];
38167            }else if(menu.events){  // menu instance
38168                return menu;
38169            }else if(typeof menu.length == 'number'){ // array of menu items?
38170                return new Roo.menu.Menu({items:menu});
38171            }else{ // otherwise, must be a config
38172                return new Roo.menu.Menu(menu);
38173            }
38174        },
38175
38176        // private
38177        unregister : function(menu){
38178            delete menus[menu.id];
38179            menu.un("beforehide", onBeforeHide);
38180            menu.un("hide", onHide);
38181            menu.un("beforeshow", onBeforeShow);
38182            menu.un("show", onShow);
38183            var g = menu.group;
38184            if(g && menu.events["checkchange"]){
38185                groups[g].remove(menu);
38186                menu.un("checkchange", onCheck);
38187            }
38188        },
38189
38190        // private
38191        registerCheckable : function(menuItem){
38192            var g = menuItem.group;
38193            if(g){
38194                if(!groups[g]){
38195                    groups[g] = [];
38196                }
38197                groups[g].push(menuItem);
38198                menuItem.on("beforecheckchange", onBeforeCheck);
38199            }
38200        },
38201
38202        // private
38203        unregisterCheckable : function(menuItem){
38204            var g = menuItem.group;
38205            if(g){
38206                groups[g].remove(menuItem);
38207                menuItem.un("beforecheckchange", onBeforeCheck);
38208            }
38209        }
38210    };
38211 }();/*
38212  * Based on:
38213  * Ext JS Library 1.1.1
38214  * Copyright(c) 2006-2007, Ext JS, LLC.
38215  *
38216  * Originally Released Under LGPL - original licence link has changed is not relivant.
38217  *
38218  * Fork - LGPL
38219  * <script type="text/javascript">
38220  */
38221  
38222
38223 /**
38224  * @class Roo.menu.BaseItem
38225  * @extends Roo.Component
38226  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38227  * management and base configuration options shared by all menu components.
38228  * @constructor
38229  * Creates a new BaseItem
38230  * @param {Object} config Configuration options
38231  */
38232 Roo.menu.BaseItem = function(config){
38233     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38234
38235     this.addEvents({
38236         /**
38237          * @event click
38238          * Fires when this item is clicked
38239          * @param {Roo.menu.BaseItem} this
38240          * @param {Roo.EventObject} e
38241          */
38242         click: true,
38243         /**
38244          * @event activate
38245          * Fires when this item is activated
38246          * @param {Roo.menu.BaseItem} this
38247          */
38248         activate : true,
38249         /**
38250          * @event deactivate
38251          * Fires when this item is deactivated
38252          * @param {Roo.menu.BaseItem} this
38253          */
38254         deactivate : true
38255     });
38256
38257     if(this.handler){
38258         this.on("click", this.handler, this.scope, true);
38259     }
38260 };
38261
38262 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38263     /**
38264      * @cfg {Function} handler
38265      * A function that will handle the click event of this menu item (defaults to undefined)
38266      */
38267     /**
38268      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38269      */
38270     canActivate : false,
38271     
38272      /**
38273      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38274      */
38275     hidden: false,
38276     
38277     /**
38278      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38279      */
38280     activeClass : "x-menu-item-active",
38281     /**
38282      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38283      */
38284     hideOnClick : true,
38285     /**
38286      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38287      */
38288     hideDelay : 100,
38289
38290     // private
38291     ctype: "Roo.menu.BaseItem",
38292
38293     // private
38294     actionMode : "container",
38295
38296     // private
38297     render : function(container, parentMenu){
38298         this.parentMenu = parentMenu;
38299         Roo.menu.BaseItem.superclass.render.call(this, container);
38300         this.container.menuItemId = this.id;
38301     },
38302
38303     // private
38304     onRender : function(container, position){
38305         this.el = Roo.get(this.el);
38306         container.dom.appendChild(this.el.dom);
38307     },
38308
38309     // private
38310     onClick : function(e){
38311         if(!this.disabled && this.fireEvent("click", this, e) !== false
38312                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38313             this.handleClick(e);
38314         }else{
38315             e.stopEvent();
38316         }
38317     },
38318
38319     // private
38320     activate : function(){
38321         if(this.disabled){
38322             return false;
38323         }
38324         var li = this.container;
38325         li.addClass(this.activeClass);
38326         this.region = li.getRegion().adjust(2, 2, -2, -2);
38327         this.fireEvent("activate", this);
38328         return true;
38329     },
38330
38331     // private
38332     deactivate : function(){
38333         this.container.removeClass(this.activeClass);
38334         this.fireEvent("deactivate", this);
38335     },
38336
38337     // private
38338     shouldDeactivate : function(e){
38339         return !this.region || !this.region.contains(e.getPoint());
38340     },
38341
38342     // private
38343     handleClick : function(e){
38344         if(this.hideOnClick){
38345             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38346         }
38347     },
38348
38349     // private
38350     expandMenu : function(autoActivate){
38351         // do nothing
38352     },
38353
38354     // private
38355     hideMenu : function(){
38356         // do nothing
38357     }
38358 });/*
38359  * Based on:
38360  * Ext JS Library 1.1.1
38361  * Copyright(c) 2006-2007, Ext JS, LLC.
38362  *
38363  * Originally Released Under LGPL - original licence link has changed is not relivant.
38364  *
38365  * Fork - LGPL
38366  * <script type="text/javascript">
38367  */
38368  
38369 /**
38370  * @class Roo.menu.Adapter
38371  * @extends Roo.menu.BaseItem
38372  * 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.
38373  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38374  * @constructor
38375  * Creates a new Adapter
38376  * @param {Object} config Configuration options
38377  */
38378 Roo.menu.Adapter = function(component, config){
38379     Roo.menu.Adapter.superclass.constructor.call(this, config);
38380     this.component = component;
38381 };
38382 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38383     // private
38384     canActivate : true,
38385
38386     // private
38387     onRender : function(container, position){
38388         this.component.render(container);
38389         this.el = this.component.getEl();
38390     },
38391
38392     // private
38393     activate : function(){
38394         if(this.disabled){
38395             return false;
38396         }
38397         this.component.focus();
38398         this.fireEvent("activate", this);
38399         return true;
38400     },
38401
38402     // private
38403     deactivate : function(){
38404         this.fireEvent("deactivate", this);
38405     },
38406
38407     // private
38408     disable : function(){
38409         this.component.disable();
38410         Roo.menu.Adapter.superclass.disable.call(this);
38411     },
38412
38413     // private
38414     enable : function(){
38415         this.component.enable();
38416         Roo.menu.Adapter.superclass.enable.call(this);
38417     }
38418 });/*
38419  * Based on:
38420  * Ext JS Library 1.1.1
38421  * Copyright(c) 2006-2007, Ext JS, LLC.
38422  *
38423  * Originally Released Under LGPL - original licence link has changed is not relivant.
38424  *
38425  * Fork - LGPL
38426  * <script type="text/javascript">
38427  */
38428
38429 /**
38430  * @class Roo.menu.TextItem
38431  * @extends Roo.menu.BaseItem
38432  * Adds a static text string to a menu, usually used as either a heading or group separator.
38433  * Note: old style constructor with text is still supported.
38434  * 
38435  * @constructor
38436  * Creates a new TextItem
38437  * @param {Object} cfg Configuration
38438  */
38439 Roo.menu.TextItem = function(cfg){
38440     if (typeof(cfg) == 'string') {
38441         this.text = cfg;
38442     } else {
38443         Roo.apply(this,cfg);
38444     }
38445     
38446     Roo.menu.TextItem.superclass.constructor.call(this);
38447 };
38448
38449 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38450     /**
38451      * @cfg {String} text Text to show on item.
38452      */
38453     text : '',
38454     
38455     /**
38456      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38457      */
38458     hideOnClick : false,
38459     /**
38460      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38461      */
38462     itemCls : "x-menu-text",
38463
38464     // private
38465     onRender : function(){
38466         var s = document.createElement("span");
38467         s.className = this.itemCls;
38468         s.innerHTML = this.text;
38469         this.el = s;
38470         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38471     }
38472 });/*
38473  * Based on:
38474  * Ext JS Library 1.1.1
38475  * Copyright(c) 2006-2007, Ext JS, LLC.
38476  *
38477  * Originally Released Under LGPL - original licence link has changed is not relivant.
38478  *
38479  * Fork - LGPL
38480  * <script type="text/javascript">
38481  */
38482
38483 /**
38484  * @class Roo.menu.Separator
38485  * @extends Roo.menu.BaseItem
38486  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38487  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38488  * @constructor
38489  * @param {Object} config Configuration options
38490  */
38491 Roo.menu.Separator = function(config){
38492     Roo.menu.Separator.superclass.constructor.call(this, config);
38493 };
38494
38495 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38496     /**
38497      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38498      */
38499     itemCls : "x-menu-sep",
38500     /**
38501      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38502      */
38503     hideOnClick : false,
38504
38505     // private
38506     onRender : function(li){
38507         var s = document.createElement("span");
38508         s.className = this.itemCls;
38509         s.innerHTML = "&#160;";
38510         this.el = s;
38511         li.addClass("x-menu-sep-li");
38512         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38513     }
38514 });/*
38515  * Based on:
38516  * Ext JS Library 1.1.1
38517  * Copyright(c) 2006-2007, Ext JS, LLC.
38518  *
38519  * Originally Released Under LGPL - original licence link has changed is not relivant.
38520  *
38521  * Fork - LGPL
38522  * <script type="text/javascript">
38523  */
38524 /**
38525  * @class Roo.menu.Item
38526  * @extends Roo.menu.BaseItem
38527  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38528  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38529  * activation and click handling.
38530  * @constructor
38531  * Creates a new Item
38532  * @param {Object} config Configuration options
38533  */
38534 Roo.menu.Item = function(config){
38535     Roo.menu.Item.superclass.constructor.call(this, config);
38536     if(this.menu){
38537         this.menu = Roo.menu.MenuMgr.get(this.menu);
38538     }
38539 };
38540 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38541     
38542     /**
38543      * @cfg {String} text
38544      * The text to show on the menu item.
38545      */
38546     text: '',
38547      /**
38548      * @cfg {String} HTML to render in menu
38549      * The text to show on the menu item (HTML version).
38550      */
38551     html: '',
38552     /**
38553      * @cfg {String} icon
38554      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38555      */
38556     icon: undefined,
38557     /**
38558      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38559      */
38560     itemCls : "x-menu-item",
38561     /**
38562      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38563      */
38564     canActivate : true,
38565     /**
38566      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38567      */
38568     showDelay: 200,
38569     // doc'd in BaseItem
38570     hideDelay: 200,
38571
38572     // private
38573     ctype: "Roo.menu.Item",
38574     
38575     // private
38576     onRender : function(container, position){
38577         var el = document.createElement("a");
38578         el.hideFocus = true;
38579         el.unselectable = "on";
38580         el.href = this.href || "#";
38581         if(this.hrefTarget){
38582             el.target = this.hrefTarget;
38583         }
38584         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38585         
38586         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38587         
38588         el.innerHTML = String.format(
38589                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38590                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38591         this.el = el;
38592         Roo.menu.Item.superclass.onRender.call(this, container, position);
38593     },
38594
38595     /**
38596      * Sets the text to display in this menu item
38597      * @param {String} text The text to display
38598      * @param {Boolean} isHTML true to indicate text is pure html.
38599      */
38600     setText : function(text, isHTML){
38601         if (isHTML) {
38602             this.html = text;
38603         } else {
38604             this.text = text;
38605             this.html = '';
38606         }
38607         if(this.rendered){
38608             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38609      
38610             this.el.update(String.format(
38611                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38612                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38613             this.parentMenu.autoWidth();
38614         }
38615     },
38616
38617     // private
38618     handleClick : function(e){
38619         if(!this.href){ // if no link defined, stop the event automatically
38620             e.stopEvent();
38621         }
38622         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38623     },
38624
38625     // private
38626     activate : function(autoExpand){
38627         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38628             this.focus();
38629             if(autoExpand){
38630                 this.expandMenu();
38631             }
38632         }
38633         return true;
38634     },
38635
38636     // private
38637     shouldDeactivate : function(e){
38638         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38639             if(this.menu && this.menu.isVisible()){
38640                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38641             }
38642             return true;
38643         }
38644         return false;
38645     },
38646
38647     // private
38648     deactivate : function(){
38649         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38650         this.hideMenu();
38651     },
38652
38653     // private
38654     expandMenu : function(autoActivate){
38655         if(!this.disabled && this.menu){
38656             clearTimeout(this.hideTimer);
38657             delete this.hideTimer;
38658             if(!this.menu.isVisible() && !this.showTimer){
38659                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38660             }else if (this.menu.isVisible() && autoActivate){
38661                 this.menu.tryActivate(0, 1);
38662             }
38663         }
38664     },
38665
38666     // private
38667     deferExpand : function(autoActivate){
38668         delete this.showTimer;
38669         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38670         if(autoActivate){
38671             this.menu.tryActivate(0, 1);
38672         }
38673     },
38674
38675     // private
38676     hideMenu : function(){
38677         clearTimeout(this.showTimer);
38678         delete this.showTimer;
38679         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38680             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38681         }
38682     },
38683
38684     // private
38685     deferHide : function(){
38686         delete this.hideTimer;
38687         this.menu.hide();
38688     }
38689 });/*
38690  * Based on:
38691  * Ext JS Library 1.1.1
38692  * Copyright(c) 2006-2007, Ext JS, LLC.
38693  *
38694  * Originally Released Under LGPL - original licence link has changed is not relivant.
38695  *
38696  * Fork - LGPL
38697  * <script type="text/javascript">
38698  */
38699  
38700 /**
38701  * @class Roo.menu.CheckItem
38702  * @extends Roo.menu.Item
38703  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38704  * @constructor
38705  * Creates a new CheckItem
38706  * @param {Object} config Configuration options
38707  */
38708 Roo.menu.CheckItem = function(config){
38709     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38710     this.addEvents({
38711         /**
38712          * @event beforecheckchange
38713          * Fires before the checked value is set, providing an opportunity to cancel if needed
38714          * @param {Roo.menu.CheckItem} this
38715          * @param {Boolean} checked The new checked value that will be set
38716          */
38717         "beforecheckchange" : true,
38718         /**
38719          * @event checkchange
38720          * Fires after the checked value has been set
38721          * @param {Roo.menu.CheckItem} this
38722          * @param {Boolean} checked The checked value that was set
38723          */
38724         "checkchange" : true
38725     });
38726     if(this.checkHandler){
38727         this.on('checkchange', this.checkHandler, this.scope);
38728     }
38729 };
38730 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38731     /**
38732      * @cfg {String} group
38733      * All check items with the same group name will automatically be grouped into a single-select
38734      * radio button group (defaults to '')
38735      */
38736     /**
38737      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38738      */
38739     itemCls : "x-menu-item x-menu-check-item",
38740     /**
38741      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38742      */
38743     groupClass : "x-menu-group-item",
38744
38745     /**
38746      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38747      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38748      * initialized with checked = true will be rendered as checked.
38749      */
38750     checked: false,
38751
38752     // private
38753     ctype: "Roo.menu.CheckItem",
38754
38755     // private
38756     onRender : function(c){
38757         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38758         if(this.group){
38759             this.el.addClass(this.groupClass);
38760         }
38761         Roo.menu.MenuMgr.registerCheckable(this);
38762         if(this.checked){
38763             this.checked = false;
38764             this.setChecked(true, true);
38765         }
38766     },
38767
38768     // private
38769     destroy : function(){
38770         if(this.rendered){
38771             Roo.menu.MenuMgr.unregisterCheckable(this);
38772         }
38773         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38774     },
38775
38776     /**
38777      * Set the checked state of this item
38778      * @param {Boolean} checked The new checked value
38779      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38780      */
38781     setChecked : function(state, suppressEvent){
38782         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38783             if(this.container){
38784                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38785             }
38786             this.checked = state;
38787             if(suppressEvent !== true){
38788                 this.fireEvent("checkchange", this, state);
38789             }
38790         }
38791     },
38792
38793     // private
38794     handleClick : function(e){
38795        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38796            this.setChecked(!this.checked);
38797        }
38798        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38799     }
38800 });/*
38801  * Based on:
38802  * Ext JS Library 1.1.1
38803  * Copyright(c) 2006-2007, Ext JS, LLC.
38804  *
38805  * Originally Released Under LGPL - original licence link has changed is not relivant.
38806  *
38807  * Fork - LGPL
38808  * <script type="text/javascript">
38809  */
38810  
38811 /**
38812  * @class Roo.menu.DateItem
38813  * @extends Roo.menu.Adapter
38814  * A menu item that wraps the {@link Roo.DatPicker} component.
38815  * @constructor
38816  * Creates a new DateItem
38817  * @param {Object} config Configuration options
38818  */
38819 Roo.menu.DateItem = function(config){
38820     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38821     /** The Roo.DatePicker object @type Roo.DatePicker */
38822     this.picker = this.component;
38823     this.addEvents({select: true});
38824     
38825     this.picker.on("render", function(picker){
38826         picker.getEl().swallowEvent("click");
38827         picker.container.addClass("x-menu-date-item");
38828     });
38829
38830     this.picker.on("select", this.onSelect, this);
38831 };
38832
38833 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38834     // private
38835     onSelect : function(picker, date){
38836         this.fireEvent("select", this, date, picker);
38837         Roo.menu.DateItem.superclass.handleClick.call(this);
38838     }
38839 });/*
38840  * Based on:
38841  * Ext JS Library 1.1.1
38842  * Copyright(c) 2006-2007, Ext JS, LLC.
38843  *
38844  * Originally Released Under LGPL - original licence link has changed is not relivant.
38845  *
38846  * Fork - LGPL
38847  * <script type="text/javascript">
38848  */
38849  
38850 /**
38851  * @class Roo.menu.ColorItem
38852  * @extends Roo.menu.Adapter
38853  * A menu item that wraps the {@link Roo.ColorPalette} component.
38854  * @constructor
38855  * Creates a new ColorItem
38856  * @param {Object} config Configuration options
38857  */
38858 Roo.menu.ColorItem = function(config){
38859     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38860     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38861     this.palette = this.component;
38862     this.relayEvents(this.palette, ["select"]);
38863     if(this.selectHandler){
38864         this.on('select', this.selectHandler, this.scope);
38865     }
38866 };
38867 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38868  * Based on:
38869  * Ext JS Library 1.1.1
38870  * Copyright(c) 2006-2007, Ext JS, LLC.
38871  *
38872  * Originally Released Under LGPL - original licence link has changed is not relivant.
38873  *
38874  * Fork - LGPL
38875  * <script type="text/javascript">
38876  */
38877  
38878
38879 /**
38880  * @class Roo.menu.DateMenu
38881  * @extends Roo.menu.Menu
38882  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38883  * @constructor
38884  * Creates a new DateMenu
38885  * @param {Object} config Configuration options
38886  */
38887 Roo.menu.DateMenu = function(config){
38888     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38889     this.plain = true;
38890     var di = new Roo.menu.DateItem(config);
38891     this.add(di);
38892     /**
38893      * The {@link Roo.DatePicker} instance for this DateMenu
38894      * @type DatePicker
38895      */
38896     this.picker = di.picker;
38897     /**
38898      * @event select
38899      * @param {DatePicker} picker
38900      * @param {Date} date
38901      */
38902     this.relayEvents(di, ["select"]);
38903     this.on('beforeshow', function(){
38904         if(this.picker){
38905             this.picker.hideMonthPicker(false);
38906         }
38907     }, this);
38908 };
38909 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38910     cls:'x-date-menu'
38911 });/*
38912  * Based on:
38913  * Ext JS Library 1.1.1
38914  * Copyright(c) 2006-2007, Ext JS, LLC.
38915  *
38916  * Originally Released Under LGPL - original licence link has changed is not relivant.
38917  *
38918  * Fork - LGPL
38919  * <script type="text/javascript">
38920  */
38921  
38922
38923 /**
38924  * @class Roo.menu.ColorMenu
38925  * @extends Roo.menu.Menu
38926  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38927  * @constructor
38928  * Creates a new ColorMenu
38929  * @param {Object} config Configuration options
38930  */
38931 Roo.menu.ColorMenu = function(config){
38932     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38933     this.plain = true;
38934     var ci = new Roo.menu.ColorItem(config);
38935     this.add(ci);
38936     /**
38937      * The {@link Roo.ColorPalette} instance for this ColorMenu
38938      * @type ColorPalette
38939      */
38940     this.palette = ci.palette;
38941     /**
38942      * @event select
38943      * @param {ColorPalette} palette
38944      * @param {String} color
38945      */
38946     this.relayEvents(ci, ["select"]);
38947 };
38948 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
38949  * Based on:
38950  * Ext JS Library 1.1.1
38951  * Copyright(c) 2006-2007, Ext JS, LLC.
38952  *
38953  * Originally Released Under LGPL - original licence link has changed is not relivant.
38954  *
38955  * Fork - LGPL
38956  * <script type="text/javascript">
38957  */
38958  
38959 /**
38960  * @class Roo.form.TextItem
38961  * @extends Roo.BoxComponent
38962  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
38963  * @constructor
38964  * Creates a new TextItem
38965  * @param {Object} config Configuration options
38966  */
38967 Roo.form.TextItem = function(config){
38968     Roo.form.TextItem.superclass.constructor.call(this, config);
38969 };
38970
38971 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
38972     
38973     /**
38974      * @cfg {String} tag the tag for this item (default div)
38975      */
38976     tag : 'div',
38977     /**
38978      * @cfg {String} html the content for this item
38979      */
38980     html : '',
38981     
38982     getAutoCreate : function()
38983     {
38984         var cfg = {
38985             id: this.id,
38986             tag: this.tag,
38987             html: this.html,
38988             cls: 'x-form-item'
38989         };
38990         
38991         return cfg;
38992         
38993     },
38994     
38995     onRender : function(ct, position)
38996     {
38997         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
38998         
38999         if(!this.el){
39000             var cfg = this.getAutoCreate();
39001             if(!cfg.name){
39002                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39003             }
39004             if (!cfg.name.length) {
39005                 delete cfg.name;
39006             }
39007             this.el = ct.createChild(cfg, position);
39008         }
39009     },
39010     /*
39011      * setHTML
39012      * @param {String} html update the Contents of the element.
39013      */
39014     setHTML : function(html)
39015     {
39016         this.fieldEl.dom.innerHTML = html;
39017     }
39018     
39019 });/*
39020  * Based on:
39021  * Ext JS Library 1.1.1
39022  * Copyright(c) 2006-2007, Ext JS, LLC.
39023  *
39024  * Originally Released Under LGPL - original licence link has changed is not relivant.
39025  *
39026  * Fork - LGPL
39027  * <script type="text/javascript">
39028  */
39029  
39030 /**
39031  * @class Roo.form.Field
39032  * @extends Roo.BoxComponent
39033  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39034  * @constructor
39035  * Creates a new Field
39036  * @param {Object} config Configuration options
39037  */
39038 Roo.form.Field = function(config){
39039     Roo.form.Field.superclass.constructor.call(this, config);
39040 };
39041
39042 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39043     /**
39044      * @cfg {String} fieldLabel Label to use when rendering a form.
39045      */
39046        /**
39047      * @cfg {String} qtip Mouse over tip
39048      */
39049      
39050     /**
39051      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39052      */
39053     invalidClass : "x-form-invalid",
39054     /**
39055      * @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")
39056      */
39057     invalidText : "The value in this field is invalid",
39058     /**
39059      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39060      */
39061     focusClass : "x-form-focus",
39062     /**
39063      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39064       automatic validation (defaults to "keyup").
39065      */
39066     validationEvent : "keyup",
39067     /**
39068      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39069      */
39070     validateOnBlur : true,
39071     /**
39072      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39073      */
39074     validationDelay : 250,
39075     /**
39076      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39077      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39078      */
39079     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39080     /**
39081      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39082      */
39083     fieldClass : "x-form-field",
39084     /**
39085      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39086      *<pre>
39087 Value         Description
39088 -----------   ----------------------------------------------------------------------
39089 qtip          Display a quick tip when the user hovers over the field
39090 title         Display a default browser title attribute popup
39091 under         Add a block div beneath the field containing the error text
39092 side          Add an error icon to the right of the field with a popup on hover
39093 [element id]  Add the error text directly to the innerHTML of the specified element
39094 </pre>
39095      */
39096     msgTarget : 'qtip',
39097     /**
39098      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39099      */
39100     msgFx : 'normal',
39101
39102     /**
39103      * @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.
39104      */
39105     readOnly : false,
39106
39107     /**
39108      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39109      */
39110     disabled : false,
39111
39112     /**
39113      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39114      */
39115     inputType : undefined,
39116     
39117     /**
39118      * @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).
39119          */
39120         tabIndex : undefined,
39121         
39122     // private
39123     isFormField : true,
39124
39125     // private
39126     hasFocus : false,
39127     /**
39128      * @property {Roo.Element} fieldEl
39129      * Element Containing the rendered Field (with label etc.)
39130      */
39131     /**
39132      * @cfg {Mixed} value A value to initialize this field with.
39133      */
39134     value : undefined,
39135
39136     /**
39137      * @cfg {String} name The field's HTML name attribute.
39138      */
39139     /**
39140      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39141      */
39142     // private
39143     loadedValue : false,
39144      
39145      
39146         // private ??
39147         initComponent : function(){
39148         Roo.form.Field.superclass.initComponent.call(this);
39149         this.addEvents({
39150             /**
39151              * @event focus
39152              * Fires when this field receives input focus.
39153              * @param {Roo.form.Field} this
39154              */
39155             focus : true,
39156             /**
39157              * @event blur
39158              * Fires when this field loses input focus.
39159              * @param {Roo.form.Field} this
39160              */
39161             blur : true,
39162             /**
39163              * @event specialkey
39164              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39165              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39166              * @param {Roo.form.Field} this
39167              * @param {Roo.EventObject} e The event object
39168              */
39169             specialkey : true,
39170             /**
39171              * @event change
39172              * Fires just before the field blurs if the field value has changed.
39173              * @param {Roo.form.Field} this
39174              * @param {Mixed} newValue The new value
39175              * @param {Mixed} oldValue The original value
39176              */
39177             change : true,
39178             /**
39179              * @event invalid
39180              * Fires after the field has been marked as invalid.
39181              * @param {Roo.form.Field} this
39182              * @param {String} msg The validation message
39183              */
39184             invalid : true,
39185             /**
39186              * @event valid
39187              * Fires after the field has been validated with no errors.
39188              * @param {Roo.form.Field} this
39189              */
39190             valid : true,
39191              /**
39192              * @event keyup
39193              * Fires after the key up
39194              * @param {Roo.form.Field} this
39195              * @param {Roo.EventObject}  e The event Object
39196              */
39197             keyup : true
39198         });
39199     },
39200
39201     /**
39202      * Returns the name attribute of the field if available
39203      * @return {String} name The field name
39204      */
39205     getName: function(){
39206          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39207     },
39208
39209     // private
39210     onRender : function(ct, position){
39211         Roo.form.Field.superclass.onRender.call(this, ct, position);
39212         if(!this.el){
39213             var cfg = this.getAutoCreate();
39214             if(!cfg.name){
39215                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39216             }
39217             if (!cfg.name.length) {
39218                 delete cfg.name;
39219             }
39220             if(this.inputType){
39221                 cfg.type = this.inputType;
39222             }
39223             this.el = ct.createChild(cfg, position);
39224         }
39225         var type = this.el.dom.type;
39226         if(type){
39227             if(type == 'password'){
39228                 type = 'text';
39229             }
39230             this.el.addClass('x-form-'+type);
39231         }
39232         if(this.readOnly){
39233             this.el.dom.readOnly = true;
39234         }
39235         if(this.tabIndex !== undefined){
39236             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39237         }
39238
39239         this.el.addClass([this.fieldClass, this.cls]);
39240         this.initValue();
39241     },
39242
39243     /**
39244      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39245      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39246      * @return {Roo.form.Field} this
39247      */
39248     applyTo : function(target){
39249         this.allowDomMove = false;
39250         this.el = Roo.get(target);
39251         this.render(this.el.dom.parentNode);
39252         return this;
39253     },
39254
39255     // private
39256     initValue : function(){
39257         if(this.value !== undefined){
39258             this.setValue(this.value);
39259         }else if(this.el.dom.value.length > 0){
39260             this.setValue(this.el.dom.value);
39261         }
39262     },
39263
39264     /**
39265      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39266      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39267      */
39268     isDirty : function() {
39269         if(this.disabled) {
39270             return false;
39271         }
39272         return String(this.getValue()) !== String(this.originalValue);
39273     },
39274
39275     /**
39276      * stores the current value in loadedValue
39277      */
39278     resetHasChanged : function()
39279     {
39280         this.loadedValue = String(this.getValue());
39281     },
39282     /**
39283      * checks the current value against the 'loaded' value.
39284      * Note - will return false if 'resetHasChanged' has not been called first.
39285      */
39286     hasChanged : function()
39287     {
39288         if(this.disabled || this.readOnly) {
39289             return false;
39290         }
39291         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39292     },
39293     
39294     
39295     
39296     // private
39297     afterRender : function(){
39298         Roo.form.Field.superclass.afterRender.call(this);
39299         this.initEvents();
39300     },
39301
39302     // private
39303     fireKey : function(e){
39304         //Roo.log('field ' + e.getKey());
39305         if(e.isNavKeyPress()){
39306             this.fireEvent("specialkey", this, e);
39307         }
39308     },
39309
39310     /**
39311      * Resets the current field value to the originally loaded value and clears any validation messages
39312      */
39313     reset : function(){
39314         this.setValue(this.resetValue);
39315         this.originalValue = this.getValue();
39316         this.clearInvalid();
39317     },
39318
39319     // private
39320     initEvents : function(){
39321         // safari killled keypress - so keydown is now used..
39322         this.el.on("keydown" , this.fireKey,  this);
39323         this.el.on("focus", this.onFocus,  this);
39324         this.el.on("blur", this.onBlur,  this);
39325         this.el.relayEvent('keyup', this);
39326
39327         // reference to original value for reset
39328         this.originalValue = this.getValue();
39329         this.resetValue =  this.getValue();
39330     },
39331
39332     // private
39333     onFocus : function(){
39334         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39335             this.el.addClass(this.focusClass);
39336         }
39337         if(!this.hasFocus){
39338             this.hasFocus = true;
39339             this.startValue = this.getValue();
39340             this.fireEvent("focus", this);
39341         }
39342     },
39343
39344     beforeBlur : Roo.emptyFn,
39345
39346     // private
39347     onBlur : function(){
39348         this.beforeBlur();
39349         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39350             this.el.removeClass(this.focusClass);
39351         }
39352         this.hasFocus = false;
39353         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39354             this.validate();
39355         }
39356         var v = this.getValue();
39357         if(String(v) !== String(this.startValue)){
39358             this.fireEvent('change', this, v, this.startValue);
39359         }
39360         this.fireEvent("blur", this);
39361     },
39362
39363     /**
39364      * Returns whether or not the field value is currently valid
39365      * @param {Boolean} preventMark True to disable marking the field invalid
39366      * @return {Boolean} True if the value is valid, else false
39367      */
39368     isValid : function(preventMark){
39369         if(this.disabled){
39370             return true;
39371         }
39372         var restore = this.preventMark;
39373         this.preventMark = preventMark === true;
39374         var v = this.validateValue(this.processValue(this.getRawValue()));
39375         this.preventMark = restore;
39376         return v;
39377     },
39378
39379     /**
39380      * Validates the field value
39381      * @return {Boolean} True if the value is valid, else false
39382      */
39383     validate : function(){
39384         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39385             this.clearInvalid();
39386             return true;
39387         }
39388         return false;
39389     },
39390
39391     processValue : function(value){
39392         return value;
39393     },
39394
39395     // private
39396     // Subclasses should provide the validation implementation by overriding this
39397     validateValue : function(value){
39398         return true;
39399     },
39400
39401     /**
39402      * Mark this field as invalid
39403      * @param {String} msg The validation message
39404      */
39405     markInvalid : function(msg){
39406         if(!this.rendered || this.preventMark){ // not rendered
39407             return;
39408         }
39409         
39410         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39411         
39412         obj.el.addClass(this.invalidClass);
39413         msg = msg || this.invalidText;
39414         switch(this.msgTarget){
39415             case 'qtip':
39416                 obj.el.dom.qtip = msg;
39417                 obj.el.dom.qclass = 'x-form-invalid-tip';
39418                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39419                     Roo.QuickTips.enable();
39420                 }
39421                 break;
39422             case 'title':
39423                 this.el.dom.title = msg;
39424                 break;
39425             case 'under':
39426                 if(!this.errorEl){
39427                     var elp = this.el.findParent('.x-form-element', 5, true);
39428                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39429                     this.errorEl.setWidth(elp.getWidth(true)-20);
39430                 }
39431                 this.errorEl.update(msg);
39432                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39433                 break;
39434             case 'side':
39435                 if(!this.errorIcon){
39436                     var elp = this.el.findParent('.x-form-element', 5, true);
39437                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39438                 }
39439                 this.alignErrorIcon();
39440                 this.errorIcon.dom.qtip = msg;
39441                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39442                 this.errorIcon.show();
39443                 this.on('resize', this.alignErrorIcon, this);
39444                 break;
39445             default:
39446                 var t = Roo.getDom(this.msgTarget);
39447                 t.innerHTML = msg;
39448                 t.style.display = this.msgDisplay;
39449                 break;
39450         }
39451         this.fireEvent('invalid', this, msg);
39452     },
39453
39454     // private
39455     alignErrorIcon : function(){
39456         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39457     },
39458
39459     /**
39460      * Clear any invalid styles/messages for this field
39461      */
39462     clearInvalid : function(){
39463         if(!this.rendered || this.preventMark){ // not rendered
39464             return;
39465         }
39466         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39467         
39468         obj.el.removeClass(this.invalidClass);
39469         switch(this.msgTarget){
39470             case 'qtip':
39471                 obj.el.dom.qtip = '';
39472                 break;
39473             case 'title':
39474                 this.el.dom.title = '';
39475                 break;
39476             case 'under':
39477                 if(this.errorEl){
39478                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39479                 }
39480                 break;
39481             case 'side':
39482                 if(this.errorIcon){
39483                     this.errorIcon.dom.qtip = '';
39484                     this.errorIcon.hide();
39485                     this.un('resize', this.alignErrorIcon, this);
39486                 }
39487                 break;
39488             default:
39489                 var t = Roo.getDom(this.msgTarget);
39490                 t.innerHTML = '';
39491                 t.style.display = 'none';
39492                 break;
39493         }
39494         this.fireEvent('valid', this);
39495     },
39496
39497     /**
39498      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39499      * @return {Mixed} value The field value
39500      */
39501     getRawValue : function(){
39502         var v = this.el.getValue();
39503         
39504         return v;
39505     },
39506
39507     /**
39508      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39509      * @return {Mixed} value The field value
39510      */
39511     getValue : function(){
39512         var v = this.el.getValue();
39513          
39514         return v;
39515     },
39516
39517     /**
39518      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39519      * @param {Mixed} value The value to set
39520      */
39521     setRawValue : function(v){
39522         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39523     },
39524
39525     /**
39526      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39527      * @param {Mixed} value The value to set
39528      */
39529     setValue : function(v){
39530         this.value = v;
39531         if(this.rendered){
39532             this.el.dom.value = (v === null || v === undefined ? '' : v);
39533              this.validate();
39534         }
39535     },
39536
39537     adjustSize : function(w, h){
39538         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39539         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39540         return s;
39541     },
39542
39543     adjustWidth : function(tag, w){
39544         tag = tag.toLowerCase();
39545         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39546             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39547                 if(tag == 'input'){
39548                     return w + 2;
39549                 }
39550                 if(tag == 'textarea'){
39551                     return w-2;
39552                 }
39553             }else if(Roo.isOpera){
39554                 if(tag == 'input'){
39555                     return w + 2;
39556                 }
39557                 if(tag == 'textarea'){
39558                     return w-2;
39559                 }
39560             }
39561         }
39562         return w;
39563     }
39564 });
39565
39566
39567 // anything other than normal should be considered experimental
39568 Roo.form.Field.msgFx = {
39569     normal : {
39570         show: function(msgEl, f){
39571             msgEl.setDisplayed('block');
39572         },
39573
39574         hide : function(msgEl, f){
39575             msgEl.setDisplayed(false).update('');
39576         }
39577     },
39578
39579     slide : {
39580         show: function(msgEl, f){
39581             msgEl.slideIn('t', {stopFx:true});
39582         },
39583
39584         hide : function(msgEl, f){
39585             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39586         }
39587     },
39588
39589     slideRight : {
39590         show: function(msgEl, f){
39591             msgEl.fixDisplay();
39592             msgEl.alignTo(f.el, 'tl-tr');
39593             msgEl.slideIn('l', {stopFx:true});
39594         },
39595
39596         hide : function(msgEl, f){
39597             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39598         }
39599     }
39600 };/*
39601  * Based on:
39602  * Ext JS Library 1.1.1
39603  * Copyright(c) 2006-2007, Ext JS, LLC.
39604  *
39605  * Originally Released Under LGPL - original licence link has changed is not relivant.
39606  *
39607  * Fork - LGPL
39608  * <script type="text/javascript">
39609  */
39610  
39611
39612 /**
39613  * @class Roo.form.TextField
39614  * @extends Roo.form.Field
39615  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39616  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39617  * @constructor
39618  * Creates a new TextField
39619  * @param {Object} config Configuration options
39620  */
39621 Roo.form.TextField = function(config){
39622     Roo.form.TextField.superclass.constructor.call(this, config);
39623     this.addEvents({
39624         /**
39625          * @event autosize
39626          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39627          * according to the default logic, but this event provides a hook for the developer to apply additional
39628          * logic at runtime to resize the field if needed.
39629              * @param {Roo.form.Field} this This text field
39630              * @param {Number} width The new field width
39631              */
39632         autosize : true
39633     });
39634 };
39635
39636 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39637     /**
39638      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39639      */
39640     grow : false,
39641     /**
39642      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39643      */
39644     growMin : 30,
39645     /**
39646      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39647      */
39648     growMax : 800,
39649     /**
39650      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39651      */
39652     vtype : null,
39653     /**
39654      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39655      */
39656     maskRe : null,
39657     /**
39658      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39659      */
39660     disableKeyFilter : false,
39661     /**
39662      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39663      */
39664     allowBlank : true,
39665     /**
39666      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39667      */
39668     minLength : 0,
39669     /**
39670      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39671      */
39672     maxLength : Number.MAX_VALUE,
39673     /**
39674      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39675      */
39676     minLengthText : "The minimum length for this field is {0}",
39677     /**
39678      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39679      */
39680     maxLengthText : "The maximum length for this field is {0}",
39681     /**
39682      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39683      */
39684     selectOnFocus : false,
39685     /**
39686      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39687      */    
39688     allowLeadingSpace : false,
39689     /**
39690      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39691      */
39692     blankText : "This field is required",
39693     /**
39694      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39695      * If available, this function will be called only after the basic validators all return true, and will be passed the
39696      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39697      */
39698     validator : null,
39699     /**
39700      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39701      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39702      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39703      */
39704     regex : null,
39705     /**
39706      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39707      */
39708     regexText : "",
39709     /**
39710      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39711      */
39712     emptyText : null,
39713    
39714
39715     // private
39716     initEvents : function()
39717     {
39718         if (this.emptyText) {
39719             this.el.attr('placeholder', this.emptyText);
39720         }
39721         
39722         Roo.form.TextField.superclass.initEvents.call(this);
39723         if(this.validationEvent == 'keyup'){
39724             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39725             this.el.on('keyup', this.filterValidation, this);
39726         }
39727         else if(this.validationEvent !== false){
39728             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39729         }
39730         
39731         if(this.selectOnFocus){
39732             this.on("focus", this.preFocus, this);
39733         }
39734         if (!this.allowLeadingSpace) {
39735             this.on('blur', this.cleanLeadingSpace, this);
39736         }
39737         
39738         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39739             this.el.on("keypress", this.filterKeys, this);
39740         }
39741         if(this.grow){
39742             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39743             this.el.on("click", this.autoSize,  this);
39744         }
39745         if(this.el.is('input[type=password]') && Roo.isSafari){
39746             this.el.on('keydown', this.SafariOnKeyDown, this);
39747         }
39748     },
39749
39750     processValue : function(value){
39751         if(this.stripCharsRe){
39752             var newValue = value.replace(this.stripCharsRe, '');
39753             if(newValue !== value){
39754                 this.setRawValue(newValue);
39755                 return newValue;
39756             }
39757         }
39758         return value;
39759     },
39760
39761     filterValidation : function(e){
39762         if(!e.isNavKeyPress()){
39763             this.validationTask.delay(this.validationDelay);
39764         }
39765     },
39766
39767     // private
39768     onKeyUp : function(e){
39769         if(!e.isNavKeyPress()){
39770             this.autoSize();
39771         }
39772     },
39773     // private - clean the leading white space
39774     cleanLeadingSpace : function(e)
39775     {
39776         if ( this.inputType == 'file') {
39777             return;
39778         }
39779         
39780         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39781     },
39782     /**
39783      * Resets the current field value to the originally-loaded value and clears any validation messages.
39784      *  
39785      */
39786     reset : function(){
39787         Roo.form.TextField.superclass.reset.call(this);
39788        
39789     }, 
39790     // private
39791     preFocus : function(){
39792         
39793         if(this.selectOnFocus){
39794             this.el.dom.select();
39795         }
39796     },
39797
39798     
39799     // private
39800     filterKeys : function(e){
39801         var k = e.getKey();
39802         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39803             return;
39804         }
39805         var c = e.getCharCode(), cc = String.fromCharCode(c);
39806         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39807             return;
39808         }
39809         if(!this.maskRe.test(cc)){
39810             e.stopEvent();
39811         }
39812     },
39813
39814     setValue : function(v){
39815         
39816         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39817         
39818         this.autoSize();
39819     },
39820
39821     /**
39822      * Validates a value according to the field's validation rules and marks the field as invalid
39823      * if the validation fails
39824      * @param {Mixed} value The value to validate
39825      * @return {Boolean} True if the value is valid, else false
39826      */
39827     validateValue : function(value){
39828         if(value.length < 1)  { // if it's blank
39829              if(this.allowBlank){
39830                 this.clearInvalid();
39831                 return true;
39832              }else{
39833                 this.markInvalid(this.blankText);
39834                 return false;
39835              }
39836         }
39837         if(value.length < this.minLength){
39838             this.markInvalid(String.format(this.minLengthText, this.minLength));
39839             return false;
39840         }
39841         if(value.length > this.maxLength){
39842             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39843             return false;
39844         }
39845         if(this.vtype){
39846             var vt = Roo.form.VTypes;
39847             if(!vt[this.vtype](value, this)){
39848                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39849                 return false;
39850             }
39851         }
39852         if(typeof this.validator == "function"){
39853             var msg = this.validator(value);
39854             if(msg !== true){
39855                 this.markInvalid(msg);
39856                 return false;
39857             }
39858         }
39859         if(this.regex && !this.regex.test(value)){
39860             this.markInvalid(this.regexText);
39861             return false;
39862         }
39863         return true;
39864     },
39865
39866     /**
39867      * Selects text in this field
39868      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39869      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39870      */
39871     selectText : function(start, end){
39872         var v = this.getRawValue();
39873         if(v.length > 0){
39874             start = start === undefined ? 0 : start;
39875             end = end === undefined ? v.length : end;
39876             var d = this.el.dom;
39877             if(d.setSelectionRange){
39878                 d.setSelectionRange(start, end);
39879             }else if(d.createTextRange){
39880                 var range = d.createTextRange();
39881                 range.moveStart("character", start);
39882                 range.moveEnd("character", v.length-end);
39883                 range.select();
39884             }
39885         }
39886     },
39887
39888     /**
39889      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39890      * This only takes effect if grow = true, and fires the autosize event.
39891      */
39892     autoSize : function(){
39893         if(!this.grow || !this.rendered){
39894             return;
39895         }
39896         if(!this.metrics){
39897             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39898         }
39899         var el = this.el;
39900         var v = el.dom.value;
39901         var d = document.createElement('div');
39902         d.appendChild(document.createTextNode(v));
39903         v = d.innerHTML;
39904         d = null;
39905         v += "&#160;";
39906         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39907         this.el.setWidth(w);
39908         this.fireEvent("autosize", this, w);
39909     },
39910     
39911     // private
39912     SafariOnKeyDown : function(event)
39913     {
39914         // this is a workaround for a password hang bug on chrome/ webkit.
39915         
39916         var isSelectAll = false;
39917         
39918         if(this.el.dom.selectionEnd > 0){
39919             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39920         }
39921         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39922             event.preventDefault();
39923             this.setValue('');
39924             return;
39925         }
39926         
39927         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39928             
39929             event.preventDefault();
39930             // this is very hacky as keydown always get's upper case.
39931             
39932             var cc = String.fromCharCode(event.getCharCode());
39933             
39934             
39935             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39936             
39937         }
39938         
39939         
39940     }
39941 });/*
39942  * Based on:
39943  * Ext JS Library 1.1.1
39944  * Copyright(c) 2006-2007, Ext JS, LLC.
39945  *
39946  * Originally Released Under LGPL - original licence link has changed is not relivant.
39947  *
39948  * Fork - LGPL
39949  * <script type="text/javascript">
39950  */
39951  
39952 /**
39953  * @class Roo.form.Hidden
39954  * @extends Roo.form.TextField
39955  * Simple Hidden element used on forms 
39956  * 
39957  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
39958  * 
39959  * @constructor
39960  * Creates a new Hidden form element.
39961  * @param {Object} config Configuration options
39962  */
39963
39964
39965
39966 // easy hidden field...
39967 Roo.form.Hidden = function(config){
39968     Roo.form.Hidden.superclass.constructor.call(this, config);
39969 };
39970   
39971 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
39972     fieldLabel:      '',
39973     inputType:      'hidden',
39974     width:          50,
39975     allowBlank:     true,
39976     labelSeparator: '',
39977     hidden:         true,
39978     itemCls :       'x-form-item-display-none'
39979
39980
39981 });
39982
39983
39984 /*
39985  * Based on:
39986  * Ext JS Library 1.1.1
39987  * Copyright(c) 2006-2007, Ext JS, LLC.
39988  *
39989  * Originally Released Under LGPL - original licence link has changed is not relivant.
39990  *
39991  * Fork - LGPL
39992  * <script type="text/javascript">
39993  */
39994  
39995 /**
39996  * @class Roo.form.TriggerField
39997  * @extends Roo.form.TextField
39998  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
39999  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40000  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40001  * for which you can provide a custom implementation.  For example:
40002  * <pre><code>
40003 var trigger = new Roo.form.TriggerField();
40004 trigger.onTriggerClick = myTriggerFn;
40005 trigger.applyTo('my-field');
40006 </code></pre>
40007  *
40008  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40009  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40010  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40011  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40012  * @constructor
40013  * Create a new TriggerField.
40014  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40015  * to the base TextField)
40016  */
40017 Roo.form.TriggerField = function(config){
40018     this.mimicing = false;
40019     Roo.form.TriggerField.superclass.constructor.call(this, config);
40020 };
40021
40022 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40023     /**
40024      * @cfg {String} triggerClass A CSS class to apply to the trigger
40025      */
40026     /**
40027      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40028      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40029      */
40030     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40031     /**
40032      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40033      */
40034     hideTrigger:false,
40035
40036     /** @cfg {Boolean} grow @hide */
40037     /** @cfg {Number} growMin @hide */
40038     /** @cfg {Number} growMax @hide */
40039
40040     /**
40041      * @hide 
40042      * @method
40043      */
40044     autoSize: Roo.emptyFn,
40045     // private
40046     monitorTab : true,
40047     // private
40048     deferHeight : true,
40049
40050     
40051     actionMode : 'wrap',
40052     // private
40053     onResize : function(w, h){
40054         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40055         if(typeof w == 'number'){
40056             var x = w - this.trigger.getWidth();
40057             this.el.setWidth(this.adjustWidth('input', x));
40058             this.trigger.setStyle('left', x+'px');
40059         }
40060     },
40061
40062     // private
40063     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40064
40065     // private
40066     getResizeEl : function(){
40067         return this.wrap;
40068     },
40069
40070     // private
40071     getPositionEl : function(){
40072         return this.wrap;
40073     },
40074
40075     // private
40076     alignErrorIcon : function(){
40077         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40078     },
40079
40080     // private
40081     onRender : function(ct, position){
40082         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40083         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40084         this.trigger = this.wrap.createChild(this.triggerConfig ||
40085                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40086         if(this.hideTrigger){
40087             this.trigger.setDisplayed(false);
40088         }
40089         this.initTrigger();
40090         if(!this.width){
40091             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40092         }
40093     },
40094
40095     // private
40096     initTrigger : function(){
40097         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40098         this.trigger.addClassOnOver('x-form-trigger-over');
40099         this.trigger.addClassOnClick('x-form-trigger-click');
40100     },
40101
40102     // private
40103     onDestroy : function(){
40104         if(this.trigger){
40105             this.trigger.removeAllListeners();
40106             this.trigger.remove();
40107         }
40108         if(this.wrap){
40109             this.wrap.remove();
40110         }
40111         Roo.form.TriggerField.superclass.onDestroy.call(this);
40112     },
40113
40114     // private
40115     onFocus : function(){
40116         Roo.form.TriggerField.superclass.onFocus.call(this);
40117         if(!this.mimicing){
40118             this.wrap.addClass('x-trigger-wrap-focus');
40119             this.mimicing = true;
40120             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40121             if(this.monitorTab){
40122                 this.el.on("keydown", this.checkTab, this);
40123             }
40124         }
40125     },
40126
40127     // private
40128     checkTab : function(e){
40129         if(e.getKey() == e.TAB){
40130             this.triggerBlur();
40131         }
40132     },
40133
40134     // private
40135     onBlur : function(){
40136         // do nothing
40137     },
40138
40139     // private
40140     mimicBlur : function(e, t){
40141         if(!this.wrap.contains(t) && this.validateBlur()){
40142             this.triggerBlur();
40143         }
40144     },
40145
40146     // private
40147     triggerBlur : function(){
40148         this.mimicing = false;
40149         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40150         if(this.monitorTab){
40151             this.el.un("keydown", this.checkTab, this);
40152         }
40153         this.wrap.removeClass('x-trigger-wrap-focus');
40154         Roo.form.TriggerField.superclass.onBlur.call(this);
40155     },
40156
40157     // private
40158     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40159     validateBlur : function(e, t){
40160         return true;
40161     },
40162
40163     // private
40164     onDisable : function(){
40165         Roo.form.TriggerField.superclass.onDisable.call(this);
40166         if(this.wrap){
40167             this.wrap.addClass('x-item-disabled');
40168         }
40169     },
40170
40171     // private
40172     onEnable : function(){
40173         Roo.form.TriggerField.superclass.onEnable.call(this);
40174         if(this.wrap){
40175             this.wrap.removeClass('x-item-disabled');
40176         }
40177     },
40178
40179     // private
40180     onShow : function(){
40181         var ae = this.getActionEl();
40182         
40183         if(ae){
40184             ae.dom.style.display = '';
40185             ae.dom.style.visibility = 'visible';
40186         }
40187     },
40188
40189     // private
40190     
40191     onHide : function(){
40192         var ae = this.getActionEl();
40193         ae.dom.style.display = 'none';
40194     },
40195
40196     /**
40197      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40198      * by an implementing function.
40199      * @method
40200      * @param {EventObject} e
40201      */
40202     onTriggerClick : Roo.emptyFn
40203 });
40204
40205 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40206 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40207 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40208 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40209     initComponent : function(){
40210         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40211
40212         this.triggerConfig = {
40213             tag:'span', cls:'x-form-twin-triggers', cn:[
40214             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40215             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40216         ]};
40217     },
40218
40219     getTrigger : function(index){
40220         return this.triggers[index];
40221     },
40222
40223     initTrigger : function(){
40224         var ts = this.trigger.select('.x-form-trigger', true);
40225         this.wrap.setStyle('overflow', 'hidden');
40226         var triggerField = this;
40227         ts.each(function(t, all, index){
40228             t.hide = function(){
40229                 var w = triggerField.wrap.getWidth();
40230                 this.dom.style.display = 'none';
40231                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40232             };
40233             t.show = function(){
40234                 var w = triggerField.wrap.getWidth();
40235                 this.dom.style.display = '';
40236                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40237             };
40238             var triggerIndex = 'Trigger'+(index+1);
40239
40240             if(this['hide'+triggerIndex]){
40241                 t.dom.style.display = 'none';
40242             }
40243             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40244             t.addClassOnOver('x-form-trigger-over');
40245             t.addClassOnClick('x-form-trigger-click');
40246         }, this);
40247         this.triggers = ts.elements;
40248     },
40249
40250     onTrigger1Click : Roo.emptyFn,
40251     onTrigger2Click : Roo.emptyFn
40252 });/*
40253  * Based on:
40254  * Ext JS Library 1.1.1
40255  * Copyright(c) 2006-2007, Ext JS, LLC.
40256  *
40257  * Originally Released Under LGPL - original licence link has changed is not relivant.
40258  *
40259  * Fork - LGPL
40260  * <script type="text/javascript">
40261  */
40262  
40263 /**
40264  * @class Roo.form.TextArea
40265  * @extends Roo.form.TextField
40266  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40267  * support for auto-sizing.
40268  * @constructor
40269  * Creates a new TextArea
40270  * @param {Object} config Configuration options
40271  */
40272 Roo.form.TextArea = function(config){
40273     Roo.form.TextArea.superclass.constructor.call(this, config);
40274     // these are provided exchanges for backwards compat
40275     // minHeight/maxHeight were replaced by growMin/growMax to be
40276     // compatible with TextField growing config values
40277     if(this.minHeight !== undefined){
40278         this.growMin = this.minHeight;
40279     }
40280     if(this.maxHeight !== undefined){
40281         this.growMax = this.maxHeight;
40282     }
40283 };
40284
40285 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40286     /**
40287      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40288      */
40289     growMin : 60,
40290     /**
40291      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40292      */
40293     growMax: 1000,
40294     /**
40295      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40296      * in the field (equivalent to setting overflow: hidden, defaults to false)
40297      */
40298     preventScrollbars: false,
40299     /**
40300      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40301      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40302      */
40303
40304     // private
40305     onRender : function(ct, position){
40306         if(!this.el){
40307             this.defaultAutoCreate = {
40308                 tag: "textarea",
40309                 style:"width:300px;height:60px;",
40310                 autocomplete: "new-password"
40311             };
40312         }
40313         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40314         if(this.grow){
40315             this.textSizeEl = Roo.DomHelper.append(document.body, {
40316                 tag: "pre", cls: "x-form-grow-sizer"
40317             });
40318             if(this.preventScrollbars){
40319                 this.el.setStyle("overflow", "hidden");
40320             }
40321             this.el.setHeight(this.growMin);
40322         }
40323     },
40324
40325     onDestroy : function(){
40326         if(this.textSizeEl){
40327             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40328         }
40329         Roo.form.TextArea.superclass.onDestroy.call(this);
40330     },
40331
40332     // private
40333     onKeyUp : function(e){
40334         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40335             this.autoSize();
40336         }
40337     },
40338
40339     /**
40340      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40341      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40342      */
40343     autoSize : function(){
40344         if(!this.grow || !this.textSizeEl){
40345             return;
40346         }
40347         var el = this.el;
40348         var v = el.dom.value;
40349         var ts = this.textSizeEl;
40350
40351         ts.innerHTML = '';
40352         ts.appendChild(document.createTextNode(v));
40353         v = ts.innerHTML;
40354
40355         Roo.fly(ts).setWidth(this.el.getWidth());
40356         if(v.length < 1){
40357             v = "&#160;&#160;";
40358         }else{
40359             if(Roo.isIE){
40360                 v = v.replace(/\n/g, '<p>&#160;</p>');
40361             }
40362             v += "&#160;\n&#160;";
40363         }
40364         ts.innerHTML = v;
40365         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40366         if(h != this.lastHeight){
40367             this.lastHeight = h;
40368             this.el.setHeight(h);
40369             this.fireEvent("autosize", this, h);
40370         }
40371     }
40372 });/*
40373  * Based on:
40374  * Ext JS Library 1.1.1
40375  * Copyright(c) 2006-2007, Ext JS, LLC.
40376  *
40377  * Originally Released Under LGPL - original licence link has changed is not relivant.
40378  *
40379  * Fork - LGPL
40380  * <script type="text/javascript">
40381  */
40382  
40383
40384 /**
40385  * @class Roo.form.NumberField
40386  * @extends Roo.form.TextField
40387  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40388  * @constructor
40389  * Creates a new NumberField
40390  * @param {Object} config Configuration options
40391  */
40392 Roo.form.NumberField = function(config){
40393     Roo.form.NumberField.superclass.constructor.call(this, config);
40394 };
40395
40396 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40397     /**
40398      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40399      */
40400     fieldClass: "x-form-field x-form-num-field",
40401     /**
40402      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40403      */
40404     allowDecimals : true,
40405     /**
40406      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40407      */
40408     decimalSeparator : ".",
40409     /**
40410      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40411      */
40412     decimalPrecision : 2,
40413     /**
40414      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40415      */
40416     allowNegative : true,
40417     /**
40418      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40419      */
40420     minValue : Number.NEGATIVE_INFINITY,
40421     /**
40422      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40423      */
40424     maxValue : Number.MAX_VALUE,
40425     /**
40426      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40427      */
40428     minText : "The minimum value for this field is {0}",
40429     /**
40430      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40431      */
40432     maxText : "The maximum value for this field is {0}",
40433     /**
40434      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40435      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40436      */
40437     nanText : "{0} is not a valid number",
40438
40439     // private
40440     initEvents : function(){
40441         Roo.form.NumberField.superclass.initEvents.call(this);
40442         var allowed = "0123456789";
40443         if(this.allowDecimals){
40444             allowed += this.decimalSeparator;
40445         }
40446         if(this.allowNegative){
40447             allowed += "-";
40448         }
40449         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40450         var keyPress = function(e){
40451             var k = e.getKey();
40452             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40453                 return;
40454             }
40455             var c = e.getCharCode();
40456             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40457                 e.stopEvent();
40458             }
40459         };
40460         this.el.on("keypress", keyPress, this);
40461     },
40462
40463     // private
40464     validateValue : function(value){
40465         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40466             return false;
40467         }
40468         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40469              return true;
40470         }
40471         var num = this.parseValue(value);
40472         if(isNaN(num)){
40473             this.markInvalid(String.format(this.nanText, value));
40474             return false;
40475         }
40476         if(num < this.minValue){
40477             this.markInvalid(String.format(this.minText, this.minValue));
40478             return false;
40479         }
40480         if(num > this.maxValue){
40481             this.markInvalid(String.format(this.maxText, this.maxValue));
40482             return false;
40483         }
40484         return true;
40485     },
40486
40487     getValue : function(){
40488         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40489     },
40490
40491     // private
40492     parseValue : function(value){
40493         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40494         return isNaN(value) ? '' : value;
40495     },
40496
40497     // private
40498     fixPrecision : function(value){
40499         var nan = isNaN(value);
40500         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40501             return nan ? '' : value;
40502         }
40503         return parseFloat(value).toFixed(this.decimalPrecision);
40504     },
40505
40506     setValue : function(v){
40507         v = this.fixPrecision(v);
40508         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40509     },
40510
40511     // private
40512     decimalPrecisionFcn : function(v){
40513         return Math.floor(v);
40514     },
40515
40516     beforeBlur : function(){
40517         var v = this.parseValue(this.getRawValue());
40518         if(v){
40519             this.setValue(v);
40520         }
40521     }
40522 });/*
40523  * Based on:
40524  * Ext JS Library 1.1.1
40525  * Copyright(c) 2006-2007, Ext JS, LLC.
40526  *
40527  * Originally Released Under LGPL - original licence link has changed is not relivant.
40528  *
40529  * Fork - LGPL
40530  * <script type="text/javascript">
40531  */
40532  
40533 /**
40534  * @class Roo.form.DateField
40535  * @extends Roo.form.TriggerField
40536  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40537 * @constructor
40538 * Create a new DateField
40539 * @param {Object} config
40540  */
40541 Roo.form.DateField = function(config)
40542 {
40543     Roo.form.DateField.superclass.constructor.call(this, config);
40544     
40545       this.addEvents({
40546          
40547         /**
40548          * @event select
40549          * Fires when a date is selected
40550              * @param {Roo.form.DateField} combo This combo box
40551              * @param {Date} date The date selected
40552              */
40553         'select' : true
40554          
40555     });
40556     
40557     
40558     if(typeof this.minValue == "string") {
40559         this.minValue = this.parseDate(this.minValue);
40560     }
40561     if(typeof this.maxValue == "string") {
40562         this.maxValue = this.parseDate(this.maxValue);
40563     }
40564     this.ddMatch = null;
40565     if(this.disabledDates){
40566         var dd = this.disabledDates;
40567         var re = "(?:";
40568         for(var i = 0; i < dd.length; i++){
40569             re += dd[i];
40570             if(i != dd.length-1) {
40571                 re += "|";
40572             }
40573         }
40574         this.ddMatch = new RegExp(re + ")");
40575     }
40576 };
40577
40578 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40579     /**
40580      * @cfg {String} format
40581      * The default date format string which can be overriden for localization support.  The format must be
40582      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40583      */
40584     format : "m/d/y",
40585     /**
40586      * @cfg {String} altFormats
40587      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40588      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40589      */
40590     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40591     /**
40592      * @cfg {Array} disabledDays
40593      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40594      */
40595     disabledDays : null,
40596     /**
40597      * @cfg {String} disabledDaysText
40598      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40599      */
40600     disabledDaysText : "Disabled",
40601     /**
40602      * @cfg {Array} disabledDates
40603      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40604      * expression so they are very powerful. Some examples:
40605      * <ul>
40606      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40607      * <li>["03/08", "09/16"] would disable those days for every year</li>
40608      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40609      * <li>["03/../2006"] would disable every day in March 2006</li>
40610      * <li>["^03"] would disable every day in every March</li>
40611      * </ul>
40612      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40613      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40614      */
40615     disabledDates : null,
40616     /**
40617      * @cfg {String} disabledDatesText
40618      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40619      */
40620     disabledDatesText : "Disabled",
40621     /**
40622      * @cfg {Date/String} minValue
40623      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40624      * valid format (defaults to null).
40625      */
40626     minValue : null,
40627     /**
40628      * @cfg {Date/String} maxValue
40629      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40630      * valid format (defaults to null).
40631      */
40632     maxValue : null,
40633     /**
40634      * @cfg {String} minText
40635      * The error text to display when the date in the cell is before minValue (defaults to
40636      * 'The date in this field must be after {minValue}').
40637      */
40638     minText : "The date in this field must be equal to or after {0}",
40639     /**
40640      * @cfg {String} maxText
40641      * The error text to display when the date in the cell is after maxValue (defaults to
40642      * 'The date in this field must be before {maxValue}').
40643      */
40644     maxText : "The date in this field must be equal to or before {0}",
40645     /**
40646      * @cfg {String} invalidText
40647      * The error text to display when the date in the field is invalid (defaults to
40648      * '{value} is not a valid date - it must be in the format {format}').
40649      */
40650     invalidText : "{0} is not a valid date - it must be in the format {1}",
40651     /**
40652      * @cfg {String} triggerClass
40653      * An additional CSS class used to style the trigger button.  The trigger will always get the
40654      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40655      * which displays a calendar icon).
40656      */
40657     triggerClass : 'x-form-date-trigger',
40658     
40659
40660     /**
40661      * @cfg {Boolean} useIso
40662      * if enabled, then the date field will use a hidden field to store the 
40663      * real value as iso formated date. default (false)
40664      */ 
40665     useIso : false,
40666     /**
40667      * @cfg {String/Object} autoCreate
40668      * A DomHelper element spec, or true for a default element spec (defaults to
40669      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40670      */ 
40671     // private
40672     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40673     
40674     // private
40675     hiddenField: false,
40676     
40677     onRender : function(ct, position)
40678     {
40679         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40680         if (this.useIso) {
40681             //this.el.dom.removeAttribute('name'); 
40682             Roo.log("Changing name?");
40683             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40684             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40685                     'before', true);
40686             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40687             // prevent input submission
40688             this.hiddenName = this.name;
40689         }
40690             
40691             
40692     },
40693     
40694     // private
40695     validateValue : function(value)
40696     {
40697         value = this.formatDate(value);
40698         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40699             Roo.log('super failed');
40700             return false;
40701         }
40702         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40703              return true;
40704         }
40705         var svalue = value;
40706         value = this.parseDate(value);
40707         if(!value){
40708             Roo.log('parse date failed' + svalue);
40709             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40710             return false;
40711         }
40712         var time = value.getTime();
40713         if(this.minValue && time < this.minValue.getTime()){
40714             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40715             return false;
40716         }
40717         if(this.maxValue && time > this.maxValue.getTime()){
40718             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40719             return false;
40720         }
40721         if(this.disabledDays){
40722             var day = value.getDay();
40723             for(var i = 0; i < this.disabledDays.length; i++) {
40724                 if(day === this.disabledDays[i]){
40725                     this.markInvalid(this.disabledDaysText);
40726                     return false;
40727                 }
40728             }
40729         }
40730         var fvalue = this.formatDate(value);
40731         if(this.ddMatch && this.ddMatch.test(fvalue)){
40732             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40733             return false;
40734         }
40735         return true;
40736     },
40737
40738     // private
40739     // Provides logic to override the default TriggerField.validateBlur which just returns true
40740     validateBlur : function(){
40741         return !this.menu || !this.menu.isVisible();
40742     },
40743     
40744     getName: function()
40745     {
40746         // returns hidden if it's set..
40747         if (!this.rendered) {return ''};
40748         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40749         
40750     },
40751
40752     /**
40753      * Returns the current date value of the date field.
40754      * @return {Date} The date value
40755      */
40756     getValue : function(){
40757         
40758         return  this.hiddenField ?
40759                 this.hiddenField.value :
40760                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40761     },
40762
40763     /**
40764      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40765      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40766      * (the default format used is "m/d/y").
40767      * <br />Usage:
40768      * <pre><code>
40769 //All of these calls set the same date value (May 4, 2006)
40770
40771 //Pass a date object:
40772 var dt = new Date('5/4/06');
40773 dateField.setValue(dt);
40774
40775 //Pass a date string (default format):
40776 dateField.setValue('5/4/06');
40777
40778 //Pass a date string (custom format):
40779 dateField.format = 'Y-m-d';
40780 dateField.setValue('2006-5-4');
40781 </code></pre>
40782      * @param {String/Date} date The date or valid date string
40783      */
40784     setValue : function(date){
40785         if (this.hiddenField) {
40786             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40787         }
40788         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40789         // make sure the value field is always stored as a date..
40790         this.value = this.parseDate(date);
40791         
40792         
40793     },
40794
40795     // private
40796     parseDate : function(value){
40797         if(!value || value instanceof Date){
40798             return value;
40799         }
40800         var v = Date.parseDate(value, this.format);
40801          if (!v && this.useIso) {
40802             v = Date.parseDate(value, 'Y-m-d');
40803         }
40804         if(!v && this.altFormats){
40805             if(!this.altFormatsArray){
40806                 this.altFormatsArray = this.altFormats.split("|");
40807             }
40808             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40809                 v = Date.parseDate(value, this.altFormatsArray[i]);
40810             }
40811         }
40812         return v;
40813     },
40814
40815     // private
40816     formatDate : function(date, fmt){
40817         return (!date || !(date instanceof Date)) ?
40818                date : date.dateFormat(fmt || this.format);
40819     },
40820
40821     // private
40822     menuListeners : {
40823         select: function(m, d){
40824             
40825             this.setValue(d);
40826             this.fireEvent('select', this, d);
40827         },
40828         show : function(){ // retain focus styling
40829             this.onFocus();
40830         },
40831         hide : function(){
40832             this.focus.defer(10, this);
40833             var ml = this.menuListeners;
40834             this.menu.un("select", ml.select,  this);
40835             this.menu.un("show", ml.show,  this);
40836             this.menu.un("hide", ml.hide,  this);
40837         }
40838     },
40839
40840     // private
40841     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40842     onTriggerClick : function(){
40843         if(this.disabled){
40844             return;
40845         }
40846         if(this.menu == null){
40847             this.menu = new Roo.menu.DateMenu();
40848         }
40849         Roo.apply(this.menu.picker,  {
40850             showClear: this.allowBlank,
40851             minDate : this.minValue,
40852             maxDate : this.maxValue,
40853             disabledDatesRE : this.ddMatch,
40854             disabledDatesText : this.disabledDatesText,
40855             disabledDays : this.disabledDays,
40856             disabledDaysText : this.disabledDaysText,
40857             format : this.useIso ? 'Y-m-d' : this.format,
40858             minText : String.format(this.minText, this.formatDate(this.minValue)),
40859             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40860         });
40861         this.menu.on(Roo.apply({}, this.menuListeners, {
40862             scope:this
40863         }));
40864         this.menu.picker.setValue(this.getValue() || new Date());
40865         this.menu.show(this.el, "tl-bl?");
40866     },
40867
40868     beforeBlur : function(){
40869         var v = this.parseDate(this.getRawValue());
40870         if(v){
40871             this.setValue(v);
40872         }
40873     },
40874
40875     /*@
40876      * overide
40877      * 
40878      */
40879     isDirty : function() {
40880         if(this.disabled) {
40881             return false;
40882         }
40883         
40884         if(typeof(this.startValue) === 'undefined'){
40885             return false;
40886         }
40887         
40888         return String(this.getValue()) !== String(this.startValue);
40889         
40890     },
40891     // @overide
40892     cleanLeadingSpace : function(e)
40893     {
40894        return;
40895     }
40896     
40897 });/*
40898  * Based on:
40899  * Ext JS Library 1.1.1
40900  * Copyright(c) 2006-2007, Ext JS, LLC.
40901  *
40902  * Originally Released Under LGPL - original licence link has changed is not relivant.
40903  *
40904  * Fork - LGPL
40905  * <script type="text/javascript">
40906  */
40907  
40908 /**
40909  * @class Roo.form.MonthField
40910  * @extends Roo.form.TriggerField
40911  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40912 * @constructor
40913 * Create a new MonthField
40914 * @param {Object} config
40915  */
40916 Roo.form.MonthField = function(config){
40917     
40918     Roo.form.MonthField.superclass.constructor.call(this, config);
40919     
40920       this.addEvents({
40921          
40922         /**
40923          * @event select
40924          * Fires when a date is selected
40925              * @param {Roo.form.MonthFieeld} combo This combo box
40926              * @param {Date} date The date selected
40927              */
40928         'select' : true
40929          
40930     });
40931     
40932     
40933     if(typeof this.minValue == "string") {
40934         this.minValue = this.parseDate(this.minValue);
40935     }
40936     if(typeof this.maxValue == "string") {
40937         this.maxValue = this.parseDate(this.maxValue);
40938     }
40939     this.ddMatch = null;
40940     if(this.disabledDates){
40941         var dd = this.disabledDates;
40942         var re = "(?:";
40943         for(var i = 0; i < dd.length; i++){
40944             re += dd[i];
40945             if(i != dd.length-1) {
40946                 re += "|";
40947             }
40948         }
40949         this.ddMatch = new RegExp(re + ")");
40950     }
40951 };
40952
40953 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
40954     /**
40955      * @cfg {String} format
40956      * The default date format string which can be overriden for localization support.  The format must be
40957      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40958      */
40959     format : "M Y",
40960     /**
40961      * @cfg {String} altFormats
40962      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40963      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40964      */
40965     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
40966     /**
40967      * @cfg {Array} disabledDays
40968      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40969      */
40970     disabledDays : [0,1,2,3,4,5,6],
40971     /**
40972      * @cfg {String} disabledDaysText
40973      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40974      */
40975     disabledDaysText : "Disabled",
40976     /**
40977      * @cfg {Array} disabledDates
40978      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40979      * expression so they are very powerful. Some examples:
40980      * <ul>
40981      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40982      * <li>["03/08", "09/16"] would disable those days for every year</li>
40983      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40984      * <li>["03/../2006"] would disable every day in March 2006</li>
40985      * <li>["^03"] would disable every day in every March</li>
40986      * </ul>
40987      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40988      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40989      */
40990     disabledDates : null,
40991     /**
40992      * @cfg {String} disabledDatesText
40993      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40994      */
40995     disabledDatesText : "Disabled",
40996     /**
40997      * @cfg {Date/String} minValue
40998      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40999      * valid format (defaults to null).
41000      */
41001     minValue : null,
41002     /**
41003      * @cfg {Date/String} maxValue
41004      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41005      * valid format (defaults to null).
41006      */
41007     maxValue : null,
41008     /**
41009      * @cfg {String} minText
41010      * The error text to display when the date in the cell is before minValue (defaults to
41011      * 'The date in this field must be after {minValue}').
41012      */
41013     minText : "The date in this field must be equal to or after {0}",
41014     /**
41015      * @cfg {String} maxTextf
41016      * The error text to display when the date in the cell is after maxValue (defaults to
41017      * 'The date in this field must be before {maxValue}').
41018      */
41019     maxText : "The date in this field must be equal to or before {0}",
41020     /**
41021      * @cfg {String} invalidText
41022      * The error text to display when the date in the field is invalid (defaults to
41023      * '{value} is not a valid date - it must be in the format {format}').
41024      */
41025     invalidText : "{0} is not a valid date - it must be in the format {1}",
41026     /**
41027      * @cfg {String} triggerClass
41028      * An additional CSS class used to style the trigger button.  The trigger will always get the
41029      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41030      * which displays a calendar icon).
41031      */
41032     triggerClass : 'x-form-date-trigger',
41033     
41034
41035     /**
41036      * @cfg {Boolean} useIso
41037      * if enabled, then the date field will use a hidden field to store the 
41038      * real value as iso formated date. default (true)
41039      */ 
41040     useIso : true,
41041     /**
41042      * @cfg {String/Object} autoCreate
41043      * A DomHelper element spec, or true for a default element spec (defaults to
41044      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41045      */ 
41046     // private
41047     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41048     
41049     // private
41050     hiddenField: false,
41051     
41052     hideMonthPicker : false,
41053     
41054     onRender : function(ct, position)
41055     {
41056         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41057         if (this.useIso) {
41058             this.el.dom.removeAttribute('name'); 
41059             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41060                     'before', true);
41061             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41062             // prevent input submission
41063             this.hiddenName = this.name;
41064         }
41065             
41066             
41067     },
41068     
41069     // private
41070     validateValue : function(value)
41071     {
41072         value = this.formatDate(value);
41073         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41074             return false;
41075         }
41076         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41077              return true;
41078         }
41079         var svalue = value;
41080         value = this.parseDate(value);
41081         if(!value){
41082             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41083             return false;
41084         }
41085         var time = value.getTime();
41086         if(this.minValue && time < this.minValue.getTime()){
41087             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41088             return false;
41089         }
41090         if(this.maxValue && time > this.maxValue.getTime()){
41091             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41092             return false;
41093         }
41094         /*if(this.disabledDays){
41095             var day = value.getDay();
41096             for(var i = 0; i < this.disabledDays.length; i++) {
41097                 if(day === this.disabledDays[i]){
41098                     this.markInvalid(this.disabledDaysText);
41099                     return false;
41100                 }
41101             }
41102         }
41103         */
41104         var fvalue = this.formatDate(value);
41105         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41106             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41107             return false;
41108         }
41109         */
41110         return true;
41111     },
41112
41113     // private
41114     // Provides logic to override the default TriggerField.validateBlur which just returns true
41115     validateBlur : function(){
41116         return !this.menu || !this.menu.isVisible();
41117     },
41118
41119     /**
41120      * Returns the current date value of the date field.
41121      * @return {Date} The date value
41122      */
41123     getValue : function(){
41124         
41125         
41126         
41127         return  this.hiddenField ?
41128                 this.hiddenField.value :
41129                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41130     },
41131
41132     /**
41133      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41134      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41135      * (the default format used is "m/d/y").
41136      * <br />Usage:
41137      * <pre><code>
41138 //All of these calls set the same date value (May 4, 2006)
41139
41140 //Pass a date object:
41141 var dt = new Date('5/4/06');
41142 monthField.setValue(dt);
41143
41144 //Pass a date string (default format):
41145 monthField.setValue('5/4/06');
41146
41147 //Pass a date string (custom format):
41148 monthField.format = 'Y-m-d';
41149 monthField.setValue('2006-5-4');
41150 </code></pre>
41151      * @param {String/Date} date The date or valid date string
41152      */
41153     setValue : function(date){
41154         Roo.log('month setValue' + date);
41155         // can only be first of month..
41156         
41157         var val = this.parseDate(date);
41158         
41159         if (this.hiddenField) {
41160             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41161         }
41162         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41163         this.value = this.parseDate(date);
41164     },
41165
41166     // private
41167     parseDate : function(value){
41168         if(!value || value instanceof Date){
41169             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41170             return value;
41171         }
41172         var v = Date.parseDate(value, this.format);
41173         if (!v && this.useIso) {
41174             v = Date.parseDate(value, 'Y-m-d');
41175         }
41176         if (v) {
41177             // 
41178             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41179         }
41180         
41181         
41182         if(!v && this.altFormats){
41183             if(!this.altFormatsArray){
41184                 this.altFormatsArray = this.altFormats.split("|");
41185             }
41186             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41187                 v = Date.parseDate(value, this.altFormatsArray[i]);
41188             }
41189         }
41190         return v;
41191     },
41192
41193     // private
41194     formatDate : function(date, fmt){
41195         return (!date || !(date instanceof Date)) ?
41196                date : date.dateFormat(fmt || this.format);
41197     },
41198
41199     // private
41200     menuListeners : {
41201         select: function(m, d){
41202             this.setValue(d);
41203             this.fireEvent('select', this, d);
41204         },
41205         show : function(){ // retain focus styling
41206             this.onFocus();
41207         },
41208         hide : function(){
41209             this.focus.defer(10, this);
41210             var ml = this.menuListeners;
41211             this.menu.un("select", ml.select,  this);
41212             this.menu.un("show", ml.show,  this);
41213             this.menu.un("hide", ml.hide,  this);
41214         }
41215     },
41216     // private
41217     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41218     onTriggerClick : function(){
41219         if(this.disabled){
41220             return;
41221         }
41222         if(this.menu == null){
41223             this.menu = new Roo.menu.DateMenu();
41224            
41225         }
41226         
41227         Roo.apply(this.menu.picker,  {
41228             
41229             showClear: this.allowBlank,
41230             minDate : this.minValue,
41231             maxDate : this.maxValue,
41232             disabledDatesRE : this.ddMatch,
41233             disabledDatesText : this.disabledDatesText,
41234             
41235             format : this.useIso ? 'Y-m-d' : this.format,
41236             minText : String.format(this.minText, this.formatDate(this.minValue)),
41237             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41238             
41239         });
41240          this.menu.on(Roo.apply({}, this.menuListeners, {
41241             scope:this
41242         }));
41243        
41244         
41245         var m = this.menu;
41246         var p = m.picker;
41247         
41248         // hide month picker get's called when we called by 'before hide';
41249         
41250         var ignorehide = true;
41251         p.hideMonthPicker  = function(disableAnim){
41252             if (ignorehide) {
41253                 return;
41254             }
41255              if(this.monthPicker){
41256                 Roo.log("hideMonthPicker called");
41257                 if(disableAnim === true){
41258                     this.monthPicker.hide();
41259                 }else{
41260                     this.monthPicker.slideOut('t', {duration:.2});
41261                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41262                     p.fireEvent("select", this, this.value);
41263                     m.hide();
41264                 }
41265             }
41266         }
41267         
41268         Roo.log('picker set value');
41269         Roo.log(this.getValue());
41270         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41271         m.show(this.el, 'tl-bl?');
41272         ignorehide  = false;
41273         // this will trigger hideMonthPicker..
41274         
41275         
41276         // hidden the day picker
41277         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41278         
41279         
41280         
41281       
41282         
41283         p.showMonthPicker.defer(100, p);
41284     
41285         
41286        
41287     },
41288
41289     beforeBlur : function(){
41290         var v = this.parseDate(this.getRawValue());
41291         if(v){
41292             this.setValue(v);
41293         }
41294     }
41295
41296     /** @cfg {Boolean} grow @hide */
41297     /** @cfg {Number} growMin @hide */
41298     /** @cfg {Number} growMax @hide */
41299     /**
41300      * @hide
41301      * @method autoSize
41302      */
41303 });/*
41304  * Based on:
41305  * Ext JS Library 1.1.1
41306  * Copyright(c) 2006-2007, Ext JS, LLC.
41307  *
41308  * Originally Released Under LGPL - original licence link has changed is not relivant.
41309  *
41310  * Fork - LGPL
41311  * <script type="text/javascript">
41312  */
41313  
41314
41315 /**
41316  * @class Roo.form.ComboBox
41317  * @extends Roo.form.TriggerField
41318  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41319  * @constructor
41320  * Create a new ComboBox.
41321  * @param {Object} config Configuration options
41322  */
41323 Roo.form.ComboBox = function(config){
41324     Roo.form.ComboBox.superclass.constructor.call(this, config);
41325     this.addEvents({
41326         /**
41327          * @event expand
41328          * Fires when the dropdown list is expanded
41329              * @param {Roo.form.ComboBox} combo This combo box
41330              */
41331         'expand' : true,
41332         /**
41333          * @event collapse
41334          * Fires when the dropdown list is collapsed
41335              * @param {Roo.form.ComboBox} combo This combo box
41336              */
41337         'collapse' : true,
41338         /**
41339          * @event beforeselect
41340          * Fires before a list item is selected. Return false to cancel the selection.
41341              * @param {Roo.form.ComboBox} combo This combo box
41342              * @param {Roo.data.Record} record The data record returned from the underlying store
41343              * @param {Number} index The index of the selected item in the dropdown list
41344              */
41345         'beforeselect' : true,
41346         /**
41347          * @event select
41348          * Fires when a list item is selected
41349              * @param {Roo.form.ComboBox} combo This combo box
41350              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41351              * @param {Number} index The index of the selected item in the dropdown list
41352              */
41353         'select' : true,
41354         /**
41355          * @event beforequery
41356          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41357          * The event object passed has these properties:
41358              * @param {Roo.form.ComboBox} combo This combo box
41359              * @param {String} query The query
41360              * @param {Boolean} forceAll true to force "all" query
41361              * @param {Boolean} cancel true to cancel the query
41362              * @param {Object} e The query event object
41363              */
41364         'beforequery': true,
41365          /**
41366          * @event add
41367          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41368              * @param {Roo.form.ComboBox} combo This combo box
41369              */
41370         'add' : true,
41371         /**
41372          * @event edit
41373          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41374              * @param {Roo.form.ComboBox} combo This combo box
41375              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41376              */
41377         'edit' : true
41378         
41379         
41380     });
41381     if(this.transform){
41382         this.allowDomMove = false;
41383         var s = Roo.getDom(this.transform);
41384         if(!this.hiddenName){
41385             this.hiddenName = s.name;
41386         }
41387         if(!this.store){
41388             this.mode = 'local';
41389             var d = [], opts = s.options;
41390             for(var i = 0, len = opts.length;i < len; i++){
41391                 var o = opts[i];
41392                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41393                 if(o.selected) {
41394                     this.value = value;
41395                 }
41396                 d.push([value, o.text]);
41397             }
41398             this.store = new Roo.data.SimpleStore({
41399                 'id': 0,
41400                 fields: ['value', 'text'],
41401                 data : d
41402             });
41403             this.valueField = 'value';
41404             this.displayField = 'text';
41405         }
41406         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41407         if(!this.lazyRender){
41408             this.target = true;
41409             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41410             s.parentNode.removeChild(s); // remove it
41411             this.render(this.el.parentNode);
41412         }else{
41413             s.parentNode.removeChild(s); // remove it
41414         }
41415
41416     }
41417     if (this.store) {
41418         this.store = Roo.factory(this.store, Roo.data);
41419     }
41420     
41421     this.selectedIndex = -1;
41422     if(this.mode == 'local'){
41423         if(config.queryDelay === undefined){
41424             this.queryDelay = 10;
41425         }
41426         if(config.minChars === undefined){
41427             this.minChars = 0;
41428         }
41429     }
41430 };
41431
41432 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41433     /**
41434      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41435      */
41436     /**
41437      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41438      * rendering into an Roo.Editor, defaults to false)
41439      */
41440     /**
41441      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41442      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41443      */
41444     /**
41445      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41446      */
41447     /**
41448      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41449      * the dropdown list (defaults to undefined, with no header element)
41450      */
41451
41452      /**
41453      * @cfg {String/Roo.Template} tpl The template to use to render the output
41454      */
41455      
41456     // private
41457     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41458     /**
41459      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41460      */
41461     listWidth: undefined,
41462     /**
41463      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41464      * mode = 'remote' or 'text' if mode = 'local')
41465      */
41466     displayField: undefined,
41467     /**
41468      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41469      * mode = 'remote' or 'value' if mode = 'local'). 
41470      * Note: use of a valueField requires the user make a selection
41471      * in order for a value to be mapped.
41472      */
41473     valueField: undefined,
41474     
41475     
41476     /**
41477      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41478      * field's data value (defaults to the underlying DOM element's name)
41479      */
41480     hiddenName: undefined,
41481     /**
41482      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41483      */
41484     listClass: '',
41485     /**
41486      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41487      */
41488     selectedClass: 'x-combo-selected',
41489     /**
41490      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41491      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41492      * which displays a downward arrow icon).
41493      */
41494     triggerClass : 'x-form-arrow-trigger',
41495     /**
41496      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41497      */
41498     shadow:'sides',
41499     /**
41500      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41501      * anchor positions (defaults to 'tl-bl')
41502      */
41503     listAlign: 'tl-bl?',
41504     /**
41505      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41506      */
41507     maxHeight: 300,
41508     /**
41509      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41510      * query specified by the allQuery config option (defaults to 'query')
41511      */
41512     triggerAction: 'query',
41513     /**
41514      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41515      * (defaults to 4, does not apply if editable = false)
41516      */
41517     minChars : 4,
41518     /**
41519      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41520      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41521      */
41522     typeAhead: false,
41523     /**
41524      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41525      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41526      */
41527     queryDelay: 500,
41528     /**
41529      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41530      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41531      */
41532     pageSize: 0,
41533     /**
41534      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41535      * when editable = true (defaults to false)
41536      */
41537     selectOnFocus:false,
41538     /**
41539      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41540      */
41541     queryParam: 'query',
41542     /**
41543      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41544      * when mode = 'remote' (defaults to 'Loading...')
41545      */
41546     loadingText: 'Loading...',
41547     /**
41548      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41549      */
41550     resizable: false,
41551     /**
41552      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41553      */
41554     handleHeight : 8,
41555     /**
41556      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41557      * traditional select (defaults to true)
41558      */
41559     editable: true,
41560     /**
41561      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41562      */
41563     allQuery: '',
41564     /**
41565      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41566      */
41567     mode: 'remote',
41568     /**
41569      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41570      * listWidth has a higher value)
41571      */
41572     minListWidth : 70,
41573     /**
41574      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41575      * allow the user to set arbitrary text into the field (defaults to false)
41576      */
41577     forceSelection:false,
41578     /**
41579      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41580      * if typeAhead = true (defaults to 250)
41581      */
41582     typeAheadDelay : 250,
41583     /**
41584      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41585      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41586      */
41587     valueNotFoundText : undefined,
41588     /**
41589      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41590      */
41591     blockFocus : false,
41592     
41593     /**
41594      * @cfg {Boolean} disableClear Disable showing of clear button.
41595      */
41596     disableClear : false,
41597     /**
41598      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41599      */
41600     alwaysQuery : false,
41601     
41602     //private
41603     addicon : false,
41604     editicon: false,
41605     
41606     // element that contains real text value.. (when hidden is used..)
41607      
41608     // private
41609     onRender : function(ct, position)
41610     {
41611         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41612         
41613         if(this.hiddenName){
41614             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41615                     'before', true);
41616             this.hiddenField.value =
41617                 this.hiddenValue !== undefined ? this.hiddenValue :
41618                 this.value !== undefined ? this.value : '';
41619
41620             // prevent input submission
41621             this.el.dom.removeAttribute('name');
41622              
41623              
41624         }
41625         
41626         if(Roo.isGecko){
41627             this.el.dom.setAttribute('autocomplete', 'off');
41628         }
41629
41630         var cls = 'x-combo-list';
41631
41632         this.list = new Roo.Layer({
41633             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41634         });
41635
41636         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41637         this.list.setWidth(lw);
41638         this.list.swallowEvent('mousewheel');
41639         this.assetHeight = 0;
41640
41641         if(this.title){
41642             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41643             this.assetHeight += this.header.getHeight();
41644         }
41645
41646         this.innerList = this.list.createChild({cls:cls+'-inner'});
41647         this.innerList.on('mouseover', this.onViewOver, this);
41648         this.innerList.on('mousemove', this.onViewMove, this);
41649         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41650         
41651         if(this.allowBlank && !this.pageSize && !this.disableClear){
41652             this.footer = this.list.createChild({cls:cls+'-ft'});
41653             this.pageTb = new Roo.Toolbar(this.footer);
41654            
41655         }
41656         if(this.pageSize){
41657             this.footer = this.list.createChild({cls:cls+'-ft'});
41658             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41659                     {pageSize: this.pageSize});
41660             
41661         }
41662         
41663         if (this.pageTb && this.allowBlank && !this.disableClear) {
41664             var _this = this;
41665             this.pageTb.add(new Roo.Toolbar.Fill(), {
41666                 cls: 'x-btn-icon x-btn-clear',
41667                 text: '&#160;',
41668                 handler: function()
41669                 {
41670                     _this.collapse();
41671                     _this.clearValue();
41672                     _this.onSelect(false, -1);
41673                 }
41674             });
41675         }
41676         if (this.footer) {
41677             this.assetHeight += this.footer.getHeight();
41678         }
41679         
41680
41681         if(!this.tpl){
41682             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41683         }
41684
41685         this.view = new Roo.View(this.innerList, this.tpl, {
41686             singleSelect:true,
41687             store: this.store,
41688             selectedClass: this.selectedClass
41689         });
41690
41691         this.view.on('click', this.onViewClick, this);
41692
41693         this.store.on('beforeload', this.onBeforeLoad, this);
41694         this.store.on('load', this.onLoad, this);
41695         this.store.on('loadexception', this.onLoadException, this);
41696
41697         if(this.resizable){
41698             this.resizer = new Roo.Resizable(this.list,  {
41699                pinned:true, handles:'se'
41700             });
41701             this.resizer.on('resize', function(r, w, h){
41702                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41703                 this.listWidth = w;
41704                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41705                 this.restrictHeight();
41706             }, this);
41707             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41708         }
41709         if(!this.editable){
41710             this.editable = true;
41711             this.setEditable(false);
41712         }  
41713         
41714         
41715         if (typeof(this.events.add.listeners) != 'undefined') {
41716             
41717             this.addicon = this.wrap.createChild(
41718                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41719        
41720             this.addicon.on('click', function(e) {
41721                 this.fireEvent('add', this);
41722             }, this);
41723         }
41724         if (typeof(this.events.edit.listeners) != 'undefined') {
41725             
41726             this.editicon = this.wrap.createChild(
41727                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41728             if (this.addicon) {
41729                 this.editicon.setStyle('margin-left', '40px');
41730             }
41731             this.editicon.on('click', function(e) {
41732                 
41733                 // we fire even  if inothing is selected..
41734                 this.fireEvent('edit', this, this.lastData );
41735                 
41736             }, this);
41737         }
41738         
41739         
41740         
41741     },
41742
41743     // private
41744     initEvents : function(){
41745         Roo.form.ComboBox.superclass.initEvents.call(this);
41746
41747         this.keyNav = new Roo.KeyNav(this.el, {
41748             "up" : function(e){
41749                 this.inKeyMode = true;
41750                 this.selectPrev();
41751             },
41752
41753             "down" : function(e){
41754                 if(!this.isExpanded()){
41755                     this.onTriggerClick();
41756                 }else{
41757                     this.inKeyMode = true;
41758                     this.selectNext();
41759                 }
41760             },
41761
41762             "enter" : function(e){
41763                 this.onViewClick();
41764                 //return true;
41765             },
41766
41767             "esc" : function(e){
41768                 this.collapse();
41769             },
41770
41771             "tab" : function(e){
41772                 this.onViewClick(false);
41773                 this.fireEvent("specialkey", this, e);
41774                 return true;
41775             },
41776
41777             scope : this,
41778
41779             doRelay : function(foo, bar, hname){
41780                 if(hname == 'down' || this.scope.isExpanded()){
41781                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41782                 }
41783                 return true;
41784             },
41785
41786             forceKeyDown: true
41787         });
41788         this.queryDelay = Math.max(this.queryDelay || 10,
41789                 this.mode == 'local' ? 10 : 250);
41790         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41791         if(this.typeAhead){
41792             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41793         }
41794         if(this.editable !== false){
41795             this.el.on("keyup", this.onKeyUp, this);
41796         }
41797         if(this.forceSelection){
41798             this.on('blur', this.doForce, this);
41799         }
41800     },
41801
41802     onDestroy : function(){
41803         if(this.view){
41804             this.view.setStore(null);
41805             this.view.el.removeAllListeners();
41806             this.view.el.remove();
41807             this.view.purgeListeners();
41808         }
41809         if(this.list){
41810             this.list.destroy();
41811         }
41812         if(this.store){
41813             this.store.un('beforeload', this.onBeforeLoad, this);
41814             this.store.un('load', this.onLoad, this);
41815             this.store.un('loadexception', this.onLoadException, this);
41816         }
41817         Roo.form.ComboBox.superclass.onDestroy.call(this);
41818     },
41819
41820     // private
41821     fireKey : function(e){
41822         if(e.isNavKeyPress() && !this.list.isVisible()){
41823             this.fireEvent("specialkey", this, e);
41824         }
41825     },
41826
41827     // private
41828     onResize: function(w, h){
41829         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41830         
41831         if(typeof w != 'number'){
41832             // we do not handle it!?!?
41833             return;
41834         }
41835         var tw = this.trigger.getWidth();
41836         tw += this.addicon ? this.addicon.getWidth() : 0;
41837         tw += this.editicon ? this.editicon.getWidth() : 0;
41838         var x = w - tw;
41839         this.el.setWidth( this.adjustWidth('input', x));
41840             
41841         this.trigger.setStyle('left', x+'px');
41842         
41843         if(this.list && this.listWidth === undefined){
41844             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41845             this.list.setWidth(lw);
41846             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41847         }
41848         
41849     
41850         
41851     },
41852
41853     /**
41854      * Allow or prevent the user from directly editing the field text.  If false is passed,
41855      * the user will only be able to select from the items defined in the dropdown list.  This method
41856      * is the runtime equivalent of setting the 'editable' config option at config time.
41857      * @param {Boolean} value True to allow the user to directly edit the field text
41858      */
41859     setEditable : function(value){
41860         if(value == this.editable){
41861             return;
41862         }
41863         this.editable = value;
41864         if(!value){
41865             this.el.dom.setAttribute('readOnly', true);
41866             this.el.on('mousedown', this.onTriggerClick,  this);
41867             this.el.addClass('x-combo-noedit');
41868         }else{
41869             this.el.dom.setAttribute('readOnly', false);
41870             this.el.un('mousedown', this.onTriggerClick,  this);
41871             this.el.removeClass('x-combo-noedit');
41872         }
41873     },
41874
41875     // private
41876     onBeforeLoad : function(){
41877         if(!this.hasFocus){
41878             return;
41879         }
41880         this.innerList.update(this.loadingText ?
41881                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41882         this.restrictHeight();
41883         this.selectedIndex = -1;
41884     },
41885
41886     // private
41887     onLoad : function(){
41888         if(!this.hasFocus){
41889             return;
41890         }
41891         if(this.store.getCount() > 0){
41892             this.expand();
41893             this.restrictHeight();
41894             if(this.lastQuery == this.allQuery){
41895                 if(this.editable){
41896                     this.el.dom.select();
41897                 }
41898                 if(!this.selectByValue(this.value, true)){
41899                     this.select(0, true);
41900                 }
41901             }else{
41902                 this.selectNext();
41903                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41904                     this.taTask.delay(this.typeAheadDelay);
41905                 }
41906             }
41907         }else{
41908             this.onEmptyResults();
41909         }
41910         //this.el.focus();
41911     },
41912     // private
41913     onLoadException : function()
41914     {
41915         this.collapse();
41916         Roo.log(this.store.reader.jsonData);
41917         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41918             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41919         }
41920         
41921         
41922     },
41923     // private
41924     onTypeAhead : function(){
41925         if(this.store.getCount() > 0){
41926             var r = this.store.getAt(0);
41927             var newValue = r.data[this.displayField];
41928             var len = newValue.length;
41929             var selStart = this.getRawValue().length;
41930             if(selStart != len){
41931                 this.setRawValue(newValue);
41932                 this.selectText(selStart, newValue.length);
41933             }
41934         }
41935     },
41936
41937     // private
41938     onSelect : function(record, index){
41939         if(this.fireEvent('beforeselect', this, record, index) !== false){
41940             this.setFromData(index > -1 ? record.data : false);
41941             this.collapse();
41942             this.fireEvent('select', this, record, index);
41943         }
41944     },
41945
41946     /**
41947      * Returns the currently selected field value or empty string if no value is set.
41948      * @return {String} value The selected value
41949      */
41950     getValue : function(){
41951         if(this.valueField){
41952             return typeof this.value != 'undefined' ? this.value : '';
41953         }
41954         return Roo.form.ComboBox.superclass.getValue.call(this);
41955     },
41956
41957     /**
41958      * Clears any text/value currently set in the field
41959      */
41960     clearValue : function(){
41961         if(this.hiddenField){
41962             this.hiddenField.value = '';
41963         }
41964         this.value = '';
41965         this.setRawValue('');
41966         this.lastSelectionText = '';
41967         
41968     },
41969
41970     /**
41971      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
41972      * will be displayed in the field.  If the value does not match the data value of an existing item,
41973      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
41974      * Otherwise the field will be blank (although the value will still be set).
41975      * @param {String} value The value to match
41976      */
41977     setValue : function(v){
41978         var text = v;
41979         if(this.valueField){
41980             var r = this.findRecord(this.valueField, v);
41981             if(r){
41982                 text = r.data[this.displayField];
41983             }else if(this.valueNotFoundText !== undefined){
41984                 text = this.valueNotFoundText;
41985             }
41986         }
41987         this.lastSelectionText = text;
41988         if(this.hiddenField){
41989             this.hiddenField.value = v;
41990         }
41991         Roo.form.ComboBox.superclass.setValue.call(this, text);
41992         this.value = v;
41993     },
41994     /**
41995      * @property {Object} the last set data for the element
41996      */
41997     
41998     lastData : false,
41999     /**
42000      * Sets the value of the field based on a object which is related to the record format for the store.
42001      * @param {Object} value the value to set as. or false on reset?
42002      */
42003     setFromData : function(o){
42004         var dv = ''; // display value
42005         var vv = ''; // value value..
42006         this.lastData = o;
42007         if (this.displayField) {
42008             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42009         } else {
42010             // this is an error condition!!!
42011             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42012         }
42013         
42014         if(this.valueField){
42015             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42016         }
42017         if(this.hiddenField){
42018             this.hiddenField.value = vv;
42019             
42020             this.lastSelectionText = dv;
42021             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42022             this.value = vv;
42023             return;
42024         }
42025         // no hidden field.. - we store the value in 'value', but still display
42026         // display field!!!!
42027         this.lastSelectionText = dv;
42028         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42029         this.value = vv;
42030         
42031         
42032     },
42033     // private
42034     reset : function(){
42035         // overridden so that last data is reset..
42036         this.setValue(this.resetValue);
42037         this.originalValue = this.getValue();
42038         this.clearInvalid();
42039         this.lastData = false;
42040         if (this.view) {
42041             this.view.clearSelections();
42042         }
42043     },
42044     // private
42045     findRecord : function(prop, value){
42046         var record;
42047         if(this.store.getCount() > 0){
42048             this.store.each(function(r){
42049                 if(r.data[prop] == value){
42050                     record = r;
42051                     return false;
42052                 }
42053                 return true;
42054             });
42055         }
42056         return record;
42057     },
42058     
42059     getName: function()
42060     {
42061         // returns hidden if it's set..
42062         if (!this.rendered) {return ''};
42063         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42064         
42065     },
42066     // private
42067     onViewMove : function(e, t){
42068         this.inKeyMode = false;
42069     },
42070
42071     // private
42072     onViewOver : function(e, t){
42073         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42074             return;
42075         }
42076         var item = this.view.findItemFromChild(t);
42077         if(item){
42078             var index = this.view.indexOf(item);
42079             this.select(index, false);
42080         }
42081     },
42082
42083     // private
42084     onViewClick : function(doFocus)
42085     {
42086         var index = this.view.getSelectedIndexes()[0];
42087         var r = this.store.getAt(index);
42088         if(r){
42089             this.onSelect(r, index);
42090         }
42091         if(doFocus !== false && !this.blockFocus){
42092             this.el.focus();
42093         }
42094     },
42095
42096     // private
42097     restrictHeight : function(){
42098         this.innerList.dom.style.height = '';
42099         var inner = this.innerList.dom;
42100         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42101         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42102         this.list.beginUpdate();
42103         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42104         this.list.alignTo(this.el, this.listAlign);
42105         this.list.endUpdate();
42106     },
42107
42108     // private
42109     onEmptyResults : function(){
42110         this.collapse();
42111     },
42112
42113     /**
42114      * Returns true if the dropdown list is expanded, else false.
42115      */
42116     isExpanded : function(){
42117         return this.list.isVisible();
42118     },
42119
42120     /**
42121      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42122      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42123      * @param {String} value The data value of the item to select
42124      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42125      * selected item if it is not currently in view (defaults to true)
42126      * @return {Boolean} True if the value matched an item in the list, else false
42127      */
42128     selectByValue : function(v, scrollIntoView){
42129         if(v !== undefined && v !== null){
42130             var r = this.findRecord(this.valueField || this.displayField, v);
42131             if(r){
42132                 this.select(this.store.indexOf(r), scrollIntoView);
42133                 return true;
42134             }
42135         }
42136         return false;
42137     },
42138
42139     /**
42140      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42141      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42142      * @param {Number} index The zero-based index of the list item to select
42143      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42144      * selected item if it is not currently in view (defaults to true)
42145      */
42146     select : function(index, scrollIntoView){
42147         this.selectedIndex = index;
42148         this.view.select(index);
42149         if(scrollIntoView !== false){
42150             var el = this.view.getNode(index);
42151             if(el){
42152                 this.innerList.scrollChildIntoView(el, false);
42153             }
42154         }
42155     },
42156
42157     // private
42158     selectNext : function(){
42159         var ct = this.store.getCount();
42160         if(ct > 0){
42161             if(this.selectedIndex == -1){
42162                 this.select(0);
42163             }else if(this.selectedIndex < ct-1){
42164                 this.select(this.selectedIndex+1);
42165             }
42166         }
42167     },
42168
42169     // private
42170     selectPrev : function(){
42171         var ct = this.store.getCount();
42172         if(ct > 0){
42173             if(this.selectedIndex == -1){
42174                 this.select(0);
42175             }else if(this.selectedIndex != 0){
42176                 this.select(this.selectedIndex-1);
42177             }
42178         }
42179     },
42180
42181     // private
42182     onKeyUp : function(e){
42183         if(this.editable !== false && !e.isSpecialKey()){
42184             this.lastKey = e.getKey();
42185             this.dqTask.delay(this.queryDelay);
42186         }
42187     },
42188
42189     // private
42190     validateBlur : function(){
42191         return !this.list || !this.list.isVisible();   
42192     },
42193
42194     // private
42195     initQuery : function(){
42196         this.doQuery(this.getRawValue());
42197     },
42198
42199     // private
42200     doForce : function(){
42201         if(this.el.dom.value.length > 0){
42202             this.el.dom.value =
42203                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42204              
42205         }
42206     },
42207
42208     /**
42209      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42210      * query allowing the query action to be canceled if needed.
42211      * @param {String} query The SQL query to execute
42212      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42213      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42214      * saved in the current store (defaults to false)
42215      */
42216     doQuery : function(q, forceAll){
42217         if(q === undefined || q === null){
42218             q = '';
42219         }
42220         var qe = {
42221             query: q,
42222             forceAll: forceAll,
42223             combo: this,
42224             cancel:false
42225         };
42226         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42227             return false;
42228         }
42229         q = qe.query;
42230         forceAll = qe.forceAll;
42231         if(forceAll === true || (q.length >= this.minChars)){
42232             if(this.lastQuery != q || this.alwaysQuery){
42233                 this.lastQuery = q;
42234                 if(this.mode == 'local'){
42235                     this.selectedIndex = -1;
42236                     if(forceAll){
42237                         this.store.clearFilter();
42238                     }else{
42239                         this.store.filter(this.displayField, q);
42240                     }
42241                     this.onLoad();
42242                 }else{
42243                     this.store.baseParams[this.queryParam] = q;
42244                     this.store.load({
42245                         params: this.getParams(q)
42246                     });
42247                     this.expand();
42248                 }
42249             }else{
42250                 this.selectedIndex = -1;
42251                 this.onLoad();   
42252             }
42253         }
42254     },
42255
42256     // private
42257     getParams : function(q){
42258         var p = {};
42259         //p[this.queryParam] = q;
42260         if(this.pageSize){
42261             p.start = 0;
42262             p.limit = this.pageSize;
42263         }
42264         return p;
42265     },
42266
42267     /**
42268      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42269      */
42270     collapse : function(){
42271         if(!this.isExpanded()){
42272             return;
42273         }
42274         this.list.hide();
42275         Roo.get(document).un('mousedown', this.collapseIf, this);
42276         Roo.get(document).un('mousewheel', this.collapseIf, this);
42277         if (!this.editable) {
42278             Roo.get(document).un('keydown', this.listKeyPress, this);
42279         }
42280         this.fireEvent('collapse', this);
42281     },
42282
42283     // private
42284     collapseIf : function(e){
42285         if(!e.within(this.wrap) && !e.within(this.list)){
42286             this.collapse();
42287         }
42288     },
42289
42290     /**
42291      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42292      */
42293     expand : function(){
42294         if(this.isExpanded() || !this.hasFocus){
42295             return;
42296         }
42297         this.list.alignTo(this.el, this.listAlign);
42298         this.list.show();
42299         Roo.get(document).on('mousedown', this.collapseIf, this);
42300         Roo.get(document).on('mousewheel', this.collapseIf, this);
42301         if (!this.editable) {
42302             Roo.get(document).on('keydown', this.listKeyPress, this);
42303         }
42304         
42305         this.fireEvent('expand', this);
42306     },
42307
42308     // private
42309     // Implements the default empty TriggerField.onTriggerClick function
42310     onTriggerClick : function(){
42311         if(this.disabled){
42312             return;
42313         }
42314         if(this.isExpanded()){
42315             this.collapse();
42316             if (!this.blockFocus) {
42317                 this.el.focus();
42318             }
42319             
42320         }else {
42321             this.hasFocus = true;
42322             if(this.triggerAction == 'all') {
42323                 this.doQuery(this.allQuery, true);
42324             } else {
42325                 this.doQuery(this.getRawValue());
42326             }
42327             if (!this.blockFocus) {
42328                 this.el.focus();
42329             }
42330         }
42331     },
42332     listKeyPress : function(e)
42333     {
42334         //Roo.log('listkeypress');
42335         // scroll to first matching element based on key pres..
42336         if (e.isSpecialKey()) {
42337             return false;
42338         }
42339         var k = String.fromCharCode(e.getKey()).toUpperCase();
42340         //Roo.log(k);
42341         var match  = false;
42342         var csel = this.view.getSelectedNodes();
42343         var cselitem = false;
42344         if (csel.length) {
42345             var ix = this.view.indexOf(csel[0]);
42346             cselitem  = this.store.getAt(ix);
42347             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42348                 cselitem = false;
42349             }
42350             
42351         }
42352         
42353         this.store.each(function(v) { 
42354             if (cselitem) {
42355                 // start at existing selection.
42356                 if (cselitem.id == v.id) {
42357                     cselitem = false;
42358                 }
42359                 return;
42360             }
42361                 
42362             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42363                 match = this.store.indexOf(v);
42364                 return false;
42365             }
42366         }, this);
42367         
42368         if (match === false) {
42369             return true; // no more action?
42370         }
42371         // scroll to?
42372         this.view.select(match);
42373         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42374         sn.scrollIntoView(sn.dom.parentNode, false);
42375     } 
42376
42377     /** 
42378     * @cfg {Boolean} grow 
42379     * @hide 
42380     */
42381     /** 
42382     * @cfg {Number} growMin 
42383     * @hide 
42384     */
42385     /** 
42386     * @cfg {Number} growMax 
42387     * @hide 
42388     */
42389     /**
42390      * @hide
42391      * @method autoSize
42392      */
42393 });/*
42394  * Copyright(c) 2010-2012, Roo J Solutions Limited
42395  *
42396  * Licence LGPL
42397  *
42398  */
42399
42400 /**
42401  * @class Roo.form.ComboBoxArray
42402  * @extends Roo.form.TextField
42403  * A facebook style adder... for lists of email / people / countries  etc...
42404  * pick multiple items from a combo box, and shows each one.
42405  *
42406  *  Fred [x]  Brian [x]  [Pick another |v]
42407  *
42408  *
42409  *  For this to work: it needs various extra information
42410  *    - normal combo problay has
42411  *      name, hiddenName
42412  *    + displayField, valueField
42413  *
42414  *    For our purpose...
42415  *
42416  *
42417  *   If we change from 'extends' to wrapping...
42418  *   
42419  *  
42420  *
42421  
42422  
42423  * @constructor
42424  * Create a new ComboBoxArray.
42425  * @param {Object} config Configuration options
42426  */
42427  
42428
42429 Roo.form.ComboBoxArray = function(config)
42430 {
42431     this.addEvents({
42432         /**
42433          * @event beforeremove
42434          * Fires before remove the value from the list
42435              * @param {Roo.form.ComboBoxArray} _self This combo box array
42436              * @param {Roo.form.ComboBoxArray.Item} item removed item
42437              */
42438         'beforeremove' : true,
42439         /**
42440          * @event remove
42441          * Fires when remove the value from the list
42442              * @param {Roo.form.ComboBoxArray} _self This combo box array
42443              * @param {Roo.form.ComboBoxArray.Item} item removed item
42444              */
42445         'remove' : true
42446         
42447         
42448     });
42449     
42450     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42451     
42452     this.items = new Roo.util.MixedCollection(false);
42453     
42454     // construct the child combo...
42455     
42456     
42457     
42458     
42459    
42460     
42461 }
42462
42463  
42464 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42465
42466     /**
42467      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42468      */
42469     
42470     lastData : false,
42471     
42472     // behavies liek a hiddne field
42473     inputType:      'hidden',
42474     /**
42475      * @cfg {Number} width The width of the box that displays the selected element
42476      */ 
42477     width:          300,
42478
42479     
42480     
42481     /**
42482      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42483      */
42484     name : false,
42485     /**
42486      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42487      */
42488     hiddenName : false,
42489       /**
42490      * @cfg {String} seperator    The value seperator normally ',' 
42491      */
42492     seperator : ',',
42493     
42494     // private the array of items that are displayed..
42495     items  : false,
42496     // private - the hidden field el.
42497     hiddenEl : false,
42498     // private - the filed el..
42499     el : false,
42500     
42501     //validateValue : function() { return true; }, // all values are ok!
42502     //onAddClick: function() { },
42503     
42504     onRender : function(ct, position) 
42505     {
42506         
42507         // create the standard hidden element
42508         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42509         
42510         
42511         // give fake names to child combo;
42512         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42513         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42514         
42515         this.combo = Roo.factory(this.combo, Roo.form);
42516         this.combo.onRender(ct, position);
42517         if (typeof(this.combo.width) != 'undefined') {
42518             this.combo.onResize(this.combo.width,0);
42519         }
42520         
42521         this.combo.initEvents();
42522         
42523         // assigned so form know we need to do this..
42524         this.store          = this.combo.store;
42525         this.valueField     = this.combo.valueField;
42526         this.displayField   = this.combo.displayField ;
42527         
42528         
42529         this.combo.wrap.addClass('x-cbarray-grp');
42530         
42531         var cbwrap = this.combo.wrap.createChild(
42532             {tag: 'div', cls: 'x-cbarray-cb'},
42533             this.combo.el.dom
42534         );
42535         
42536              
42537         this.hiddenEl = this.combo.wrap.createChild({
42538             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42539         });
42540         this.el = this.combo.wrap.createChild({
42541             tag: 'input',  type:'hidden' , name: this.name, value : ''
42542         });
42543          //   this.el.dom.removeAttribute("name");
42544         
42545         
42546         this.outerWrap = this.combo.wrap;
42547         this.wrap = cbwrap;
42548         
42549         this.outerWrap.setWidth(this.width);
42550         this.outerWrap.dom.removeChild(this.el.dom);
42551         
42552         this.wrap.dom.appendChild(this.el.dom);
42553         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42554         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42555         
42556         this.combo.trigger.setStyle('position','relative');
42557         this.combo.trigger.setStyle('left', '0px');
42558         this.combo.trigger.setStyle('top', '2px');
42559         
42560         this.combo.el.setStyle('vertical-align', 'text-bottom');
42561         
42562         //this.trigger.setStyle('vertical-align', 'top');
42563         
42564         // this should use the code from combo really... on('add' ....)
42565         if (this.adder) {
42566             
42567         
42568             this.adder = this.outerWrap.createChild(
42569                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42570             var _t = this;
42571             this.adder.on('click', function(e) {
42572                 _t.fireEvent('adderclick', this, e);
42573             }, _t);
42574         }
42575         //var _t = this;
42576         //this.adder.on('click', this.onAddClick, _t);
42577         
42578         
42579         this.combo.on('select', function(cb, rec, ix) {
42580             this.addItem(rec.data);
42581             
42582             cb.setValue('');
42583             cb.el.dom.value = '';
42584             //cb.lastData = rec.data;
42585             // add to list
42586             
42587         }, this);
42588         
42589         
42590     },
42591     
42592     
42593     getName: function()
42594     {
42595         // returns hidden if it's set..
42596         if (!this.rendered) {return ''};
42597         return  this.hiddenName ? this.hiddenName : this.name;
42598         
42599     },
42600     
42601     
42602     onResize: function(w, h){
42603         
42604         return;
42605         // not sure if this is needed..
42606         //this.combo.onResize(w,h);
42607         
42608         if(typeof w != 'number'){
42609             // we do not handle it!?!?
42610             return;
42611         }
42612         var tw = this.combo.trigger.getWidth();
42613         tw += this.addicon ? this.addicon.getWidth() : 0;
42614         tw += this.editicon ? this.editicon.getWidth() : 0;
42615         var x = w - tw;
42616         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42617             
42618         this.combo.trigger.setStyle('left', '0px');
42619         
42620         if(this.list && this.listWidth === undefined){
42621             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42622             this.list.setWidth(lw);
42623             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42624         }
42625         
42626     
42627         
42628     },
42629     
42630     addItem: function(rec)
42631     {
42632         var valueField = this.combo.valueField;
42633         var displayField = this.combo.displayField;
42634         
42635         if (this.items.indexOfKey(rec[valueField]) > -1) {
42636             //console.log("GOT " + rec.data.id);
42637             return;
42638         }
42639         
42640         var x = new Roo.form.ComboBoxArray.Item({
42641             //id : rec[this.idField],
42642             data : rec,
42643             displayField : displayField ,
42644             tipField : displayField ,
42645             cb : this
42646         });
42647         // use the 
42648         this.items.add(rec[valueField],x);
42649         // add it before the element..
42650         this.updateHiddenEl();
42651         x.render(this.outerWrap, this.wrap.dom);
42652         // add the image handler..
42653     },
42654     
42655     updateHiddenEl : function()
42656     {
42657         this.validate();
42658         if (!this.hiddenEl) {
42659             return;
42660         }
42661         var ar = [];
42662         var idField = this.combo.valueField;
42663         
42664         this.items.each(function(f) {
42665             ar.push(f.data[idField]);
42666         });
42667         this.hiddenEl.dom.value = ar.join(this.seperator);
42668         this.validate();
42669     },
42670     
42671     reset : function()
42672     {
42673         this.items.clear();
42674         
42675         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42676            el.remove();
42677         });
42678         
42679         this.el.dom.value = '';
42680         if (this.hiddenEl) {
42681             this.hiddenEl.dom.value = '';
42682         }
42683         
42684     },
42685     getValue: function()
42686     {
42687         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42688     },
42689     setValue: function(v) // not a valid action - must use addItems..
42690     {
42691         
42692         this.reset();
42693          
42694         if (this.store.isLocal && (typeof(v) == 'string')) {
42695             // then we can use the store to find the values..
42696             // comma seperated at present.. this needs to allow JSON based encoding..
42697             this.hiddenEl.value  = v;
42698             var v_ar = [];
42699             Roo.each(v.split(this.seperator), function(k) {
42700                 Roo.log("CHECK " + this.valueField + ',' + k);
42701                 var li = this.store.query(this.valueField, k);
42702                 if (!li.length) {
42703                     return;
42704                 }
42705                 var add = {};
42706                 add[this.valueField] = k;
42707                 add[this.displayField] = li.item(0).data[this.displayField];
42708                 
42709                 this.addItem(add);
42710             }, this) 
42711              
42712         }
42713         if (typeof(v) == 'object' ) {
42714             // then let's assume it's an array of objects..
42715             Roo.each(v, function(l) {
42716                 var add = l;
42717                 if (typeof(l) == 'string') {
42718                     add = {};
42719                     add[this.valueField] = l;
42720                     add[this.displayField] = l
42721                 }
42722                 this.addItem(add);
42723             }, this);
42724              
42725         }
42726         
42727         
42728     },
42729     setFromData: function(v)
42730     {
42731         // this recieves an object, if setValues is called.
42732         this.reset();
42733         this.el.dom.value = v[this.displayField];
42734         this.hiddenEl.dom.value = v[this.valueField];
42735         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42736             return;
42737         }
42738         var kv = v[this.valueField];
42739         var dv = v[this.displayField];
42740         kv = typeof(kv) != 'string' ? '' : kv;
42741         dv = typeof(dv) != 'string' ? '' : dv;
42742         
42743         
42744         var keys = kv.split(this.seperator);
42745         var display = dv.split(this.seperator);
42746         for (var i = 0 ; i < keys.length; i++) {
42747             add = {};
42748             add[this.valueField] = keys[i];
42749             add[this.displayField] = display[i];
42750             this.addItem(add);
42751         }
42752       
42753         
42754     },
42755     
42756     /**
42757      * Validates the combox array value
42758      * @return {Boolean} True if the value is valid, else false
42759      */
42760     validate : function(){
42761         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42762             this.clearInvalid();
42763             return true;
42764         }
42765         return false;
42766     },
42767     
42768     validateValue : function(value){
42769         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42770         
42771     },
42772     
42773     /*@
42774      * overide
42775      * 
42776      */
42777     isDirty : function() {
42778         if(this.disabled) {
42779             return false;
42780         }
42781         
42782         try {
42783             var d = Roo.decode(String(this.originalValue));
42784         } catch (e) {
42785             return String(this.getValue()) !== String(this.originalValue);
42786         }
42787         
42788         var originalValue = [];
42789         
42790         for (var i = 0; i < d.length; i++){
42791             originalValue.push(d[i][this.valueField]);
42792         }
42793         
42794         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42795         
42796     }
42797     
42798 });
42799
42800
42801
42802 /**
42803  * @class Roo.form.ComboBoxArray.Item
42804  * @extends Roo.BoxComponent
42805  * A selected item in the list
42806  *  Fred [x]  Brian [x]  [Pick another |v]
42807  * 
42808  * @constructor
42809  * Create a new item.
42810  * @param {Object} config Configuration options
42811  */
42812  
42813 Roo.form.ComboBoxArray.Item = function(config) {
42814     config.id = Roo.id();
42815     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42816 }
42817
42818 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42819     data : {},
42820     cb: false,
42821     displayField : false,
42822     tipField : false,
42823     
42824     
42825     defaultAutoCreate : {
42826         tag: 'div',
42827         cls: 'x-cbarray-item',
42828         cn : [ 
42829             { tag: 'div' },
42830             {
42831                 tag: 'img',
42832                 width:16,
42833                 height : 16,
42834                 src : Roo.BLANK_IMAGE_URL ,
42835                 align: 'center'
42836             }
42837         ]
42838         
42839     },
42840     
42841  
42842     onRender : function(ct, position)
42843     {
42844         Roo.form.Field.superclass.onRender.call(this, ct, position);
42845         
42846         if(!this.el){
42847             var cfg = this.getAutoCreate();
42848             this.el = ct.createChild(cfg, position);
42849         }
42850         
42851         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42852         
42853         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42854             this.cb.renderer(this.data) :
42855             String.format('{0}',this.data[this.displayField]);
42856         
42857             
42858         this.el.child('div').dom.setAttribute('qtip',
42859                         String.format('{0}',this.data[this.tipField])
42860         );
42861         
42862         this.el.child('img').on('click', this.remove, this);
42863         
42864     },
42865    
42866     remove : function()
42867     {
42868         if(this.cb.disabled){
42869             return;
42870         }
42871         
42872         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42873             this.cb.items.remove(this);
42874             this.el.child('img').un('click', this.remove, this);
42875             this.el.remove();
42876             this.cb.updateHiddenEl();
42877
42878             this.cb.fireEvent('remove', this.cb, this);
42879         }
42880         
42881     }
42882 });/*
42883  * RooJS Library 1.1.1
42884  * Copyright(c) 2008-2011  Alan Knowles
42885  *
42886  * License - LGPL
42887  */
42888  
42889
42890 /**
42891  * @class Roo.form.ComboNested
42892  * @extends Roo.form.ComboBox
42893  * A combobox for that allows selection of nested items in a list,
42894  * eg.
42895  *
42896  *  Book
42897  *    -> red
42898  *    -> green
42899  *  Table
42900  *    -> square
42901  *      ->red
42902  *      ->green
42903  *    -> rectangle
42904  *      ->green
42905  *      
42906  * 
42907  * @constructor
42908  * Create a new ComboNested
42909  * @param {Object} config Configuration options
42910  */
42911 Roo.form.ComboNested = function(config){
42912     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42913     // should verify some data...
42914     // like
42915     // hiddenName = required..
42916     // displayField = required
42917     // valudField == required
42918     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42919     var _t = this;
42920     Roo.each(req, function(e) {
42921         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42922             throw "Roo.form.ComboNested : missing value for: " + e;
42923         }
42924     });
42925      
42926     
42927 };
42928
42929 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42930    
42931     /*
42932      * @config {Number} max Number of columns to show
42933      */
42934     
42935     maxColumns : 3,
42936    
42937     list : null, // the outermost div..
42938     innerLists : null, // the
42939     views : null,
42940     stores : null,
42941     // private
42942     loadingChildren : false,
42943     
42944     onRender : function(ct, position)
42945     {
42946         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
42947         
42948         if(this.hiddenName){
42949             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42950                     'before', true);
42951             this.hiddenField.value =
42952                 this.hiddenValue !== undefined ? this.hiddenValue :
42953                 this.value !== undefined ? this.value : '';
42954
42955             // prevent input submission
42956             this.el.dom.removeAttribute('name');
42957              
42958              
42959         }
42960         
42961         if(Roo.isGecko){
42962             this.el.dom.setAttribute('autocomplete', 'off');
42963         }
42964
42965         var cls = 'x-combo-list';
42966
42967         this.list = new Roo.Layer({
42968             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42969         });
42970
42971         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42972         this.list.setWidth(lw);
42973         this.list.swallowEvent('mousewheel');
42974         this.assetHeight = 0;
42975
42976         if(this.title){
42977             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42978             this.assetHeight += this.header.getHeight();
42979         }
42980         this.innerLists = [];
42981         this.views = [];
42982         this.stores = [];
42983         for (var i =0 ; i < this.maxColumns; i++) {
42984             this.onRenderList( cls, i);
42985         }
42986         
42987         // always needs footer, as we are going to have an 'OK' button.
42988         this.footer = this.list.createChild({cls:cls+'-ft'});
42989         this.pageTb = new Roo.Toolbar(this.footer);  
42990         var _this = this;
42991         this.pageTb.add(  {
42992             
42993             text: 'Done',
42994             handler: function()
42995             {
42996                 _this.collapse();
42997             }
42998         });
42999         
43000         if ( this.allowBlank && !this.disableClear) {
43001             
43002             this.pageTb.add(new Roo.Toolbar.Fill(), {
43003                 cls: 'x-btn-icon x-btn-clear',
43004                 text: '&#160;',
43005                 handler: function()
43006                 {
43007                     _this.collapse();
43008                     _this.clearValue();
43009                     _this.onSelect(false, -1);
43010                 }
43011             });
43012         }
43013         if (this.footer) {
43014             this.assetHeight += this.footer.getHeight();
43015         }
43016         
43017     },
43018     onRenderList : function (  cls, i)
43019     {
43020         
43021         var lw = Math.floor(
43022                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43023         );
43024         
43025         this.list.setWidth(lw); // default to '1'
43026
43027         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43028         //il.on('mouseover', this.onViewOver, this, { list:  i });
43029         //il.on('mousemove', this.onViewMove, this, { list:  i });
43030         il.setWidth(lw);
43031         il.setStyle({ 'overflow-x' : 'hidden'});
43032
43033         if(!this.tpl){
43034             this.tpl = new Roo.Template({
43035                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43036                 isEmpty: function (value, allValues) {
43037                     //Roo.log(value);
43038                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43039                     return dl ? 'has-children' : 'no-children'
43040                 }
43041             });
43042         }
43043         
43044         var store  = this.store;
43045         if (i > 0) {
43046             store  = new Roo.data.SimpleStore({
43047                 //fields : this.store.reader.meta.fields,
43048                 reader : this.store.reader,
43049                 data : [ ]
43050             });
43051         }
43052         this.stores[i]  = store;
43053                   
43054         var view = this.views[i] = new Roo.View(
43055             il,
43056             this.tpl,
43057             {
43058                 singleSelect:true,
43059                 store: store,
43060                 selectedClass: this.selectedClass
43061             }
43062         );
43063         view.getEl().setWidth(lw);
43064         view.getEl().setStyle({
43065             position: i < 1 ? 'relative' : 'absolute',
43066             top: 0,
43067             left: (i * lw ) + 'px',
43068             display : i > 0 ? 'none' : 'block'
43069         });
43070         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43071         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43072         //view.on('click', this.onViewClick, this, { list : i });
43073
43074         store.on('beforeload', this.onBeforeLoad, this);
43075         store.on('load',  this.onLoad, this, { list  : i});
43076         store.on('loadexception', this.onLoadException, this);
43077
43078         // hide the other vies..
43079         
43080         
43081         
43082     },
43083       
43084     restrictHeight : function()
43085     {
43086         var mh = 0;
43087         Roo.each(this.innerLists, function(il,i) {
43088             var el = this.views[i].getEl();
43089             el.dom.style.height = '';
43090             var inner = el.dom;
43091             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43092             // only adjust heights on other ones..
43093             mh = Math.max(h, mh);
43094             if (i < 1) {
43095                 
43096                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43097                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43098                
43099             }
43100             
43101             
43102         }, this);
43103         
43104         this.list.beginUpdate();
43105         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43106         this.list.alignTo(this.el, this.listAlign);
43107         this.list.endUpdate();
43108         
43109     },
43110      
43111     
43112     // -- store handlers..
43113     // private
43114     onBeforeLoad : function()
43115     {
43116         if(!this.hasFocus){
43117             return;
43118         }
43119         this.innerLists[0].update(this.loadingText ?
43120                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43121         this.restrictHeight();
43122         this.selectedIndex = -1;
43123     },
43124     // private
43125     onLoad : function(a,b,c,d)
43126     {
43127         if (!this.loadingChildren) {
43128             // then we are loading the top level. - hide the children
43129             for (var i = 1;i < this.views.length; i++) {
43130                 this.views[i].getEl().setStyle({ display : 'none' });
43131             }
43132             var lw = Math.floor(
43133                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43134             );
43135         
43136              this.list.setWidth(lw); // default to '1'
43137
43138             
43139         }
43140         if(!this.hasFocus){
43141             return;
43142         }
43143         
43144         if(this.store.getCount() > 0) {
43145             this.expand();
43146             this.restrictHeight();   
43147         } else {
43148             this.onEmptyResults();
43149         }
43150         
43151         if (!this.loadingChildren) {
43152             this.selectActive();
43153         }
43154         /*
43155         this.stores[1].loadData([]);
43156         this.stores[2].loadData([]);
43157         this.views
43158         */    
43159     
43160         //this.el.focus();
43161     },
43162     
43163     
43164     // private
43165     onLoadException : function()
43166     {
43167         this.collapse();
43168         Roo.log(this.store.reader.jsonData);
43169         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43170             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43171         }
43172         
43173         
43174     },
43175     // no cleaning of leading spaces on blur here.
43176     cleanLeadingSpace : function(e) { },
43177     
43178
43179     onSelectChange : function (view, sels, opts )
43180     {
43181         var ix = view.getSelectedIndexes();
43182          
43183         if (opts.list > this.maxColumns - 2) {
43184             if (view.store.getCount()<  1) {
43185                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43186
43187             } else  {
43188                 if (ix.length) {
43189                     // used to clear ?? but if we are loading unselected 
43190                     this.setFromData(view.store.getAt(ix[0]).data);
43191                 }
43192                 
43193             }
43194             
43195             return;
43196         }
43197         
43198         if (!ix.length) {
43199             // this get's fired when trigger opens..
43200            // this.setFromData({});
43201             var str = this.stores[opts.list+1];
43202             str.data.clear(); // removeall wihtout the fire events..
43203             return;
43204         }
43205         
43206         var rec = view.store.getAt(ix[0]);
43207          
43208         this.setFromData(rec.data);
43209         this.fireEvent('select', this, rec, ix[0]);
43210         
43211         var lw = Math.floor(
43212              (
43213                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43214              ) / this.maxColumns
43215         );
43216         this.loadingChildren = true;
43217         this.stores[opts.list+1].loadDataFromChildren( rec );
43218         this.loadingChildren = false;
43219         var dl = this.stores[opts.list+1]. getTotalCount();
43220         
43221         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43222         
43223         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43224         for (var i = opts.list+2; i < this.views.length;i++) {
43225             this.views[i].getEl().setStyle({ display : 'none' });
43226         }
43227         
43228         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43229         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43230         
43231         if (this.isLoading) {
43232            // this.selectActive(opts.list);
43233         }
43234          
43235     },
43236     
43237     
43238     
43239     
43240     onDoubleClick : function()
43241     {
43242         this.collapse(); //??
43243     },
43244     
43245      
43246     
43247     
43248     
43249     // private
43250     recordToStack : function(store, prop, value, stack)
43251     {
43252         var cstore = new Roo.data.SimpleStore({
43253             //fields : this.store.reader.meta.fields, // we need array reader.. for
43254             reader : this.store.reader,
43255             data : [ ]
43256         });
43257         var _this = this;
43258         var record  = false;
43259         var srec = false;
43260         if(store.getCount() < 1){
43261             return false;
43262         }
43263         store.each(function(r){
43264             if(r.data[prop] == value){
43265                 record = r;
43266             srec = r;
43267                 return false;
43268             }
43269             if (r.data.cn && r.data.cn.length) {
43270                 cstore.loadDataFromChildren( r);
43271                 var cret = _this.recordToStack(cstore, prop, value, stack);
43272                 if (cret !== false) {
43273                     record = cret;
43274                     srec = r;
43275                     return false;
43276                 }
43277             }
43278              
43279             return true;
43280         });
43281         if (record == false) {
43282             return false
43283         }
43284         stack.unshift(srec);
43285         return record;
43286     },
43287     
43288     /*
43289      * find the stack of stores that match our value.
43290      *
43291      * 
43292      */
43293     
43294     selectActive : function ()
43295     {
43296         // if store is not loaded, then we will need to wait for that to happen first.
43297         var stack = [];
43298         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43299         for (var i = 0; i < stack.length; i++ ) {
43300             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43301         }
43302         
43303     }
43304         
43305          
43306     
43307     
43308     
43309     
43310 });/*
43311  * Based on:
43312  * Ext JS Library 1.1.1
43313  * Copyright(c) 2006-2007, Ext JS, LLC.
43314  *
43315  * Originally Released Under LGPL - original licence link has changed is not relivant.
43316  *
43317  * Fork - LGPL
43318  * <script type="text/javascript">
43319  */
43320 /**
43321  * @class Roo.form.Checkbox
43322  * @extends Roo.form.Field
43323  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43324  * @constructor
43325  * Creates a new Checkbox
43326  * @param {Object} config Configuration options
43327  */
43328 Roo.form.Checkbox = function(config){
43329     Roo.form.Checkbox.superclass.constructor.call(this, config);
43330     this.addEvents({
43331         /**
43332          * @event check
43333          * Fires when the checkbox is checked or unchecked.
43334              * @param {Roo.form.Checkbox} this This checkbox
43335              * @param {Boolean} checked The new checked value
43336              */
43337         check : true
43338     });
43339 };
43340
43341 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43342     /**
43343      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43344      */
43345     focusClass : undefined,
43346     /**
43347      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43348      */
43349     fieldClass: "x-form-field",
43350     /**
43351      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43352      */
43353     checked: false,
43354     /**
43355      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43356      * {tag: "input", type: "checkbox", autocomplete: "off"})
43357      */
43358     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43359     /**
43360      * @cfg {String} boxLabel The text that appears beside the checkbox
43361      */
43362     boxLabel : "",
43363     /**
43364      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43365      */  
43366     inputValue : '1',
43367     /**
43368      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43369      */
43370      valueOff: '0', // value when not checked..
43371
43372     actionMode : 'viewEl', 
43373     //
43374     // private
43375     itemCls : 'x-menu-check-item x-form-item',
43376     groupClass : 'x-menu-group-item',
43377     inputType : 'hidden',
43378     
43379     
43380     inSetChecked: false, // check that we are not calling self...
43381     
43382     inputElement: false, // real input element?
43383     basedOn: false, // ????
43384     
43385     isFormField: true, // not sure where this is needed!!!!
43386
43387     onResize : function(){
43388         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43389         if(!this.boxLabel){
43390             this.el.alignTo(this.wrap, 'c-c');
43391         }
43392     },
43393
43394     initEvents : function(){
43395         Roo.form.Checkbox.superclass.initEvents.call(this);
43396         this.el.on("click", this.onClick,  this);
43397         this.el.on("change", this.onClick,  this);
43398     },
43399
43400
43401     getResizeEl : function(){
43402         return this.wrap;
43403     },
43404
43405     getPositionEl : function(){
43406         return this.wrap;
43407     },
43408
43409     // private
43410     onRender : function(ct, position){
43411         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43412         /*
43413         if(this.inputValue !== undefined){
43414             this.el.dom.value = this.inputValue;
43415         }
43416         */
43417         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43418         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43419         var viewEl = this.wrap.createChild({ 
43420             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43421         this.viewEl = viewEl;   
43422         this.wrap.on('click', this.onClick,  this); 
43423         
43424         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43425         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43426         
43427         
43428         
43429         if(this.boxLabel){
43430             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43431         //    viewEl.on('click', this.onClick,  this); 
43432         }
43433         //if(this.checked){
43434             this.setChecked(this.checked);
43435         //}else{
43436             //this.checked = this.el.dom;
43437         //}
43438
43439     },
43440
43441     // private
43442     initValue : Roo.emptyFn,
43443
43444     /**
43445      * Returns the checked state of the checkbox.
43446      * @return {Boolean} True if checked, else false
43447      */
43448     getValue : function(){
43449         if(this.el){
43450             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43451         }
43452         return this.valueOff;
43453         
43454     },
43455
43456         // private
43457     onClick : function(){ 
43458         if (this.disabled) {
43459             return;
43460         }
43461         this.setChecked(!this.checked);
43462
43463         //if(this.el.dom.checked != this.checked){
43464         //    this.setValue(this.el.dom.checked);
43465        // }
43466     },
43467
43468     /**
43469      * Sets the checked state of the checkbox.
43470      * On is always based on a string comparison between inputValue and the param.
43471      * @param {Boolean/String} value - the value to set 
43472      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43473      */
43474     setValue : function(v,suppressEvent){
43475         
43476         
43477         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43478         //if(this.el && this.el.dom){
43479         //    this.el.dom.checked = this.checked;
43480         //    this.el.dom.defaultChecked = this.checked;
43481         //}
43482         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43483         //this.fireEvent("check", this, this.checked);
43484     },
43485     // private..
43486     setChecked : function(state,suppressEvent)
43487     {
43488         if (this.inSetChecked) {
43489             this.checked = state;
43490             return;
43491         }
43492         
43493     
43494         if(this.wrap){
43495             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43496         }
43497         this.checked = state;
43498         if(suppressEvent !== true){
43499             this.fireEvent('check', this, state);
43500         }
43501         this.inSetChecked = true;
43502         this.el.dom.value = state ? this.inputValue : this.valueOff;
43503         this.inSetChecked = false;
43504         
43505     },
43506     // handle setting of hidden value by some other method!!?!?
43507     setFromHidden: function()
43508     {
43509         if(!this.el){
43510             return;
43511         }
43512         //console.log("SET FROM HIDDEN");
43513         //alert('setFrom hidden');
43514         this.setValue(this.el.dom.value);
43515     },
43516     
43517     onDestroy : function()
43518     {
43519         if(this.viewEl){
43520             Roo.get(this.viewEl).remove();
43521         }
43522          
43523         Roo.form.Checkbox.superclass.onDestroy.call(this);
43524     },
43525     
43526     setBoxLabel : function(str)
43527     {
43528         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43529     }
43530
43531 });/*
43532  * Based on:
43533  * Ext JS Library 1.1.1
43534  * Copyright(c) 2006-2007, Ext JS, LLC.
43535  *
43536  * Originally Released Under LGPL - original licence link has changed is not relivant.
43537  *
43538  * Fork - LGPL
43539  * <script type="text/javascript">
43540  */
43541  
43542 /**
43543  * @class Roo.form.Radio
43544  * @extends Roo.form.Checkbox
43545  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43546  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43547  * @constructor
43548  * Creates a new Radio
43549  * @param {Object} config Configuration options
43550  */
43551 Roo.form.Radio = function(){
43552     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43553 };
43554 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43555     inputType: 'radio',
43556
43557     /**
43558      * If this radio is part of a group, it will return the selected value
43559      * @return {String}
43560      */
43561     getGroupValue : function(){
43562         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43563     },
43564     
43565     
43566     onRender : function(ct, position){
43567         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43568         
43569         if(this.inputValue !== undefined){
43570             this.el.dom.value = this.inputValue;
43571         }
43572          
43573         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43574         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43575         //var viewEl = this.wrap.createChild({ 
43576         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43577         //this.viewEl = viewEl;   
43578         //this.wrap.on('click', this.onClick,  this); 
43579         
43580         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43581         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43582         
43583         
43584         
43585         if(this.boxLabel){
43586             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43587         //    viewEl.on('click', this.onClick,  this); 
43588         }
43589          if(this.checked){
43590             this.el.dom.checked =   'checked' ;
43591         }
43592          
43593     } 
43594     
43595     
43596 });//<script type="text/javascript">
43597
43598 /*
43599  * Based  Ext JS Library 1.1.1
43600  * Copyright(c) 2006-2007, Ext JS, LLC.
43601  * LGPL
43602  *
43603  */
43604  
43605 /**
43606  * @class Roo.HtmlEditorCore
43607  * @extends Roo.Component
43608  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43609  *
43610  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43611  */
43612
43613 Roo.HtmlEditorCore = function(config){
43614     
43615     
43616     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43617     
43618     
43619     this.addEvents({
43620         /**
43621          * @event initialize
43622          * Fires when the editor is fully initialized (including the iframe)
43623          * @param {Roo.HtmlEditorCore} this
43624          */
43625         initialize: true,
43626         /**
43627          * @event activate
43628          * Fires when the editor is first receives the focus. Any insertion must wait
43629          * until after this event.
43630          * @param {Roo.HtmlEditorCore} this
43631          */
43632         activate: true,
43633          /**
43634          * @event beforesync
43635          * Fires before the textarea is updated with content from the editor iframe. Return false
43636          * to cancel the sync.
43637          * @param {Roo.HtmlEditorCore} this
43638          * @param {String} html
43639          */
43640         beforesync: true,
43641          /**
43642          * @event beforepush
43643          * Fires before the iframe editor is updated with content from the textarea. Return false
43644          * to cancel the push.
43645          * @param {Roo.HtmlEditorCore} this
43646          * @param {String} html
43647          */
43648         beforepush: true,
43649          /**
43650          * @event sync
43651          * Fires when the textarea is updated with content from the editor iframe.
43652          * @param {Roo.HtmlEditorCore} this
43653          * @param {String} html
43654          */
43655         sync: true,
43656          /**
43657          * @event push
43658          * Fires when the iframe editor is updated with content from the textarea.
43659          * @param {Roo.HtmlEditorCore} this
43660          * @param {String} html
43661          */
43662         push: true,
43663         
43664         /**
43665          * @event editorevent
43666          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43667          * @param {Roo.HtmlEditorCore} this
43668          */
43669         editorevent: true
43670         
43671     });
43672     
43673     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43674     
43675     // defaults : white / black...
43676     this.applyBlacklists();
43677     
43678     
43679     
43680 };
43681
43682
43683 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43684
43685
43686      /**
43687      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43688      */
43689     
43690     owner : false,
43691     
43692      /**
43693      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43694      *                        Roo.resizable.
43695      */
43696     resizable : false,
43697      /**
43698      * @cfg {Number} height (in pixels)
43699      */   
43700     height: 300,
43701    /**
43702      * @cfg {Number} width (in pixels)
43703      */   
43704     width: 500,
43705     
43706     /**
43707      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43708      * 
43709      */
43710     stylesheets: false,
43711     
43712     // id of frame..
43713     frameId: false,
43714     
43715     // private properties
43716     validationEvent : false,
43717     deferHeight: true,
43718     initialized : false,
43719     activated : false,
43720     sourceEditMode : false,
43721     onFocus : Roo.emptyFn,
43722     iframePad:3,
43723     hideMode:'offsets',
43724     
43725     clearUp: true,
43726     
43727     // blacklist + whitelisted elements..
43728     black: false,
43729     white: false,
43730      
43731     bodyCls : '',
43732
43733     /**
43734      * Protected method that will not generally be called directly. It
43735      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43736      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43737      */
43738     getDocMarkup : function(){
43739         // body styles..
43740         var st = '';
43741         
43742         // inherit styels from page...?? 
43743         if (this.stylesheets === false) {
43744             
43745             Roo.get(document.head).select('style').each(function(node) {
43746                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43747             });
43748             
43749             Roo.get(document.head).select('link').each(function(node) { 
43750                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43751             });
43752             
43753         } else if (!this.stylesheets.length) {
43754                 // simple..
43755                 st = '<style type="text/css">' +
43756                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43757                    '</style>';
43758         } else {
43759             for (var i in this.stylesheets) { 
43760                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43761             }
43762             
43763         }
43764         
43765         st +=  '<style type="text/css">' +
43766             'IMG { cursor: pointer } ' +
43767         '</style>';
43768
43769         var cls = 'roo-htmleditor-body';
43770         
43771         if(this.bodyCls.length){
43772             cls += ' ' + this.bodyCls;
43773         }
43774         
43775         return '<html><head>' + st  +
43776             //<style type="text/css">' +
43777             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43778             //'</style>' +
43779             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43780     },
43781
43782     // private
43783     onRender : function(ct, position)
43784     {
43785         var _t = this;
43786         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43787         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43788         
43789         
43790         this.el.dom.style.border = '0 none';
43791         this.el.dom.setAttribute('tabIndex', -1);
43792         this.el.addClass('x-hidden hide');
43793         
43794         
43795         
43796         if(Roo.isIE){ // fix IE 1px bogus margin
43797             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43798         }
43799        
43800         
43801         this.frameId = Roo.id();
43802         
43803          
43804         
43805         var iframe = this.owner.wrap.createChild({
43806             tag: 'iframe',
43807             cls: 'form-control', // bootstrap..
43808             id: this.frameId,
43809             name: this.frameId,
43810             frameBorder : 'no',
43811             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43812         }, this.el
43813         );
43814         
43815         
43816         this.iframe = iframe.dom;
43817
43818          this.assignDocWin();
43819         
43820         this.doc.designMode = 'on';
43821        
43822         this.doc.open();
43823         this.doc.write(this.getDocMarkup());
43824         this.doc.close();
43825
43826         
43827         var task = { // must defer to wait for browser to be ready
43828             run : function(){
43829                 //console.log("run task?" + this.doc.readyState);
43830                 this.assignDocWin();
43831                 if(this.doc.body || this.doc.readyState == 'complete'){
43832                     try {
43833                         this.doc.designMode="on";
43834                     } catch (e) {
43835                         return;
43836                     }
43837                     Roo.TaskMgr.stop(task);
43838                     this.initEditor.defer(10, this);
43839                 }
43840             },
43841             interval : 10,
43842             duration: 10000,
43843             scope: this
43844         };
43845         Roo.TaskMgr.start(task);
43846
43847     },
43848
43849     // private
43850     onResize : function(w, h)
43851     {
43852          Roo.log('resize: ' +w + ',' + h );
43853         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43854         if(!this.iframe){
43855             return;
43856         }
43857         if(typeof w == 'number'){
43858             
43859             this.iframe.style.width = w + 'px';
43860         }
43861         if(typeof h == 'number'){
43862             
43863             this.iframe.style.height = h + 'px';
43864             if(this.doc){
43865                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43866             }
43867         }
43868         
43869     },
43870
43871     /**
43872      * Toggles the editor between standard and source edit mode.
43873      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43874      */
43875     toggleSourceEdit : function(sourceEditMode){
43876         
43877         this.sourceEditMode = sourceEditMode === true;
43878         
43879         if(this.sourceEditMode){
43880  
43881             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43882             
43883         }else{
43884             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43885             //this.iframe.className = '';
43886             this.deferFocus();
43887         }
43888         //this.setSize(this.owner.wrap.getSize());
43889         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43890     },
43891
43892     
43893   
43894
43895     /**
43896      * Protected method that will not generally be called directly. If you need/want
43897      * custom HTML cleanup, this is the method you should override.
43898      * @param {String} html The HTML to be cleaned
43899      * return {String} The cleaned HTML
43900      */
43901     cleanHtml : function(html){
43902         html = String(html);
43903         if(html.length > 5){
43904             if(Roo.isSafari){ // strip safari nonsense
43905                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43906             }
43907         }
43908         if(html == '&nbsp;'){
43909             html = '';
43910         }
43911         return html;
43912     },
43913
43914     /**
43915      * HTML Editor -> Textarea
43916      * Protected method that will not generally be called directly. Syncs the contents
43917      * of the editor iframe with the textarea.
43918      */
43919     syncValue : function(){
43920         if(this.initialized){
43921             var bd = (this.doc.body || this.doc.documentElement);
43922             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43923             var html = bd.innerHTML;
43924             if(Roo.isSafari){
43925                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43926                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43927                 if(m && m[1]){
43928                     html = '<div style="'+m[0]+'">' + html + '</div>';
43929                 }
43930             }
43931             html = this.cleanHtml(html);
43932             // fix up the special chars.. normaly like back quotes in word...
43933             // however we do not want to do this with chinese..
43934             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43935                 
43936                 var cc = match.charCodeAt();
43937
43938                 // Get the character value, handling surrogate pairs
43939                 if (match.length == 2) {
43940                     // It's a surrogate pair, calculate the Unicode code point
43941                     var high = match.charCodeAt(0) - 0xD800;
43942                     var low  = match.charCodeAt(1) - 0xDC00;
43943                     cc = (high * 0x400) + low + 0x10000;
43944                 }  else if (
43945                     (cc >= 0x4E00 && cc < 0xA000 ) ||
43946                     (cc >= 0x3400 && cc < 0x4E00 ) ||
43947                     (cc >= 0xf900 && cc < 0xfb00 )
43948                 ) {
43949                         return match;
43950                 }  
43951          
43952                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
43953                 return "&#" + cc + ";";
43954                 
43955                 
43956             });
43957             
43958             
43959              
43960             if(this.owner.fireEvent('beforesync', this, html) !== false){
43961                 this.el.dom.value = html;
43962                 this.owner.fireEvent('sync', this, html);
43963             }
43964         }
43965     },
43966
43967     /**
43968      * Protected method that will not generally be called directly. Pushes the value of the textarea
43969      * into the iframe editor.
43970      */
43971     pushValue : function(){
43972         if(this.initialized){
43973             var v = this.el.dom.value.trim();
43974             
43975 //            if(v.length < 1){
43976 //                v = '&#160;';
43977 //            }
43978             
43979             if(this.owner.fireEvent('beforepush', this, v) !== false){
43980                 var d = (this.doc.body || this.doc.documentElement);
43981                 d.innerHTML = v;
43982                 this.cleanUpPaste();
43983                 this.el.dom.value = d.innerHTML;
43984                 this.owner.fireEvent('push', this, v);
43985             }
43986         }
43987     },
43988
43989     // private
43990     deferFocus : function(){
43991         this.focus.defer(10, this);
43992     },
43993
43994     // doc'ed in Field
43995     focus : function(){
43996         if(this.win && !this.sourceEditMode){
43997             this.win.focus();
43998         }else{
43999             this.el.focus();
44000         }
44001     },
44002     
44003     assignDocWin: function()
44004     {
44005         var iframe = this.iframe;
44006         
44007          if(Roo.isIE){
44008             this.doc = iframe.contentWindow.document;
44009             this.win = iframe.contentWindow;
44010         } else {
44011 //            if (!Roo.get(this.frameId)) {
44012 //                return;
44013 //            }
44014 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44015 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44016             
44017             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44018                 return;
44019             }
44020             
44021             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44022             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44023         }
44024     },
44025     
44026     // private
44027     initEditor : function(){
44028         //console.log("INIT EDITOR");
44029         this.assignDocWin();
44030         
44031         
44032         
44033         this.doc.designMode="on";
44034         this.doc.open();
44035         this.doc.write(this.getDocMarkup());
44036         this.doc.close();
44037         
44038         var dbody = (this.doc.body || this.doc.documentElement);
44039         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44040         // this copies styles from the containing element into thsi one..
44041         // not sure why we need all of this..
44042         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44043         
44044         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44045         //ss['background-attachment'] = 'fixed'; // w3c
44046         dbody.bgProperties = 'fixed'; // ie
44047         //Roo.DomHelper.applyStyles(dbody, ss);
44048         Roo.EventManager.on(this.doc, {
44049             //'mousedown': this.onEditorEvent,
44050             'mouseup': this.onEditorEvent,
44051             'dblclick': this.onEditorEvent,
44052             'click': this.onEditorEvent,
44053             'keyup': this.onEditorEvent,
44054             buffer:100,
44055             scope: this
44056         });
44057         if(Roo.isGecko){
44058             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44059         }
44060         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44061             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44062         }
44063         this.initialized = true;
44064
44065         this.owner.fireEvent('initialize', this);
44066         this.pushValue();
44067     },
44068
44069     // private
44070     onDestroy : function(){
44071         
44072         
44073         
44074         if(this.rendered){
44075             
44076             //for (var i =0; i < this.toolbars.length;i++) {
44077             //    // fixme - ask toolbars for heights?
44078             //    this.toolbars[i].onDestroy();
44079            // }
44080             
44081             //this.wrap.dom.innerHTML = '';
44082             //this.wrap.remove();
44083         }
44084     },
44085
44086     // private
44087     onFirstFocus : function(){
44088         
44089         this.assignDocWin();
44090         
44091         
44092         this.activated = true;
44093          
44094     
44095         if(Roo.isGecko){ // prevent silly gecko errors
44096             this.win.focus();
44097             var s = this.win.getSelection();
44098             if(!s.focusNode || s.focusNode.nodeType != 3){
44099                 var r = s.getRangeAt(0);
44100                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44101                 r.collapse(true);
44102                 this.deferFocus();
44103             }
44104             try{
44105                 this.execCmd('useCSS', true);
44106                 this.execCmd('styleWithCSS', false);
44107             }catch(e){}
44108         }
44109         this.owner.fireEvent('activate', this);
44110     },
44111
44112     // private
44113     adjustFont: function(btn){
44114         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44115         //if(Roo.isSafari){ // safari
44116         //    adjust *= 2;
44117        // }
44118         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44119         if(Roo.isSafari){ // safari
44120             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44121             v =  (v < 10) ? 10 : v;
44122             v =  (v > 48) ? 48 : v;
44123             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44124             
44125         }
44126         
44127         
44128         v = Math.max(1, v+adjust);
44129         
44130         this.execCmd('FontSize', v  );
44131     },
44132
44133     onEditorEvent : function(e)
44134     {
44135         this.owner.fireEvent('editorevent', this, e);
44136       //  this.updateToolbar();
44137         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44138     },
44139
44140     insertTag : function(tg)
44141     {
44142         // could be a bit smarter... -> wrap the current selected tRoo..
44143         if (tg.toLowerCase() == 'span' ||
44144             tg.toLowerCase() == 'code' ||
44145             tg.toLowerCase() == 'sup' ||
44146             tg.toLowerCase() == 'sub' 
44147             ) {
44148             
44149             range = this.createRange(this.getSelection());
44150             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44151             wrappingNode.appendChild(range.extractContents());
44152             range.insertNode(wrappingNode);
44153
44154             return;
44155             
44156             
44157             
44158         }
44159         this.execCmd("formatblock",   tg);
44160         
44161     },
44162     
44163     insertText : function(txt)
44164     {
44165         
44166         
44167         var range = this.createRange();
44168         range.deleteContents();
44169                //alert(Sender.getAttribute('label'));
44170                
44171         range.insertNode(this.doc.createTextNode(txt));
44172     } ,
44173     
44174      
44175
44176     /**
44177      * Executes a Midas editor command on the editor document and performs necessary focus and
44178      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44179      * @param {String} cmd The Midas command
44180      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44181      */
44182     relayCmd : function(cmd, value){
44183         this.win.focus();
44184         this.execCmd(cmd, value);
44185         this.owner.fireEvent('editorevent', this);
44186         //this.updateToolbar();
44187         this.owner.deferFocus();
44188     },
44189
44190     /**
44191      * Executes a Midas editor command directly on the editor document.
44192      * For visual commands, you should use {@link #relayCmd} instead.
44193      * <b>This should only be called after the editor is initialized.</b>
44194      * @param {String} cmd The Midas command
44195      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44196      */
44197     execCmd : function(cmd, value){
44198         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44199         this.syncValue();
44200     },
44201  
44202  
44203    
44204     /**
44205      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44206      * to insert tRoo.
44207      * @param {String} text | dom node.. 
44208      */
44209     insertAtCursor : function(text)
44210     {
44211         
44212         if(!this.activated){
44213             return;
44214         }
44215         /*
44216         if(Roo.isIE){
44217             this.win.focus();
44218             var r = this.doc.selection.createRange();
44219             if(r){
44220                 r.collapse(true);
44221                 r.pasteHTML(text);
44222                 this.syncValue();
44223                 this.deferFocus();
44224             
44225             }
44226             return;
44227         }
44228         */
44229         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44230             this.win.focus();
44231             
44232             
44233             // from jquery ui (MIT licenced)
44234             var range, node;
44235             var win = this.win;
44236             
44237             if (win.getSelection && win.getSelection().getRangeAt) {
44238                 range = win.getSelection().getRangeAt(0);
44239                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44240                 range.insertNode(node);
44241             } else if (win.document.selection && win.document.selection.createRange) {
44242                 // no firefox support
44243                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44244                 win.document.selection.createRange().pasteHTML(txt);
44245             } else {
44246                 // no firefox support
44247                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44248                 this.execCmd('InsertHTML', txt);
44249             } 
44250             
44251             this.syncValue();
44252             
44253             this.deferFocus();
44254         }
44255     },
44256  // private
44257     mozKeyPress : function(e){
44258         if(e.ctrlKey){
44259             var c = e.getCharCode(), cmd;
44260           
44261             if(c > 0){
44262                 c = String.fromCharCode(c).toLowerCase();
44263                 switch(c){
44264                     case 'b':
44265                         cmd = 'bold';
44266                         break;
44267                     case 'i':
44268                         cmd = 'italic';
44269                         break;
44270                     
44271                     case 'u':
44272                         cmd = 'underline';
44273                         break;
44274                     
44275                     case 'v':
44276                         this.cleanUpPaste.defer(100, this);
44277                         return;
44278                         
44279                 }
44280                 if(cmd){
44281                     this.win.focus();
44282                     this.execCmd(cmd);
44283                     this.deferFocus();
44284                     e.preventDefault();
44285                 }
44286                 
44287             }
44288         }
44289     },
44290
44291     // private
44292     fixKeys : function(){ // load time branching for fastest keydown performance
44293         if(Roo.isIE){
44294             return function(e){
44295                 var k = e.getKey(), r;
44296                 if(k == e.TAB){
44297                     e.stopEvent();
44298                     r = this.doc.selection.createRange();
44299                     if(r){
44300                         r.collapse(true);
44301                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44302                         this.deferFocus();
44303                     }
44304                     return;
44305                 }
44306                 
44307                 if(k == e.ENTER){
44308                     r = this.doc.selection.createRange();
44309                     if(r){
44310                         var target = r.parentElement();
44311                         if(!target || target.tagName.toLowerCase() != 'li'){
44312                             e.stopEvent();
44313                             r.pasteHTML('<br />');
44314                             r.collapse(false);
44315                             r.select();
44316                         }
44317                     }
44318                 }
44319                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44320                     this.cleanUpPaste.defer(100, this);
44321                     return;
44322                 }
44323                 
44324                 
44325             };
44326         }else if(Roo.isOpera){
44327             return function(e){
44328                 var k = e.getKey();
44329                 if(k == e.TAB){
44330                     e.stopEvent();
44331                     this.win.focus();
44332                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44333                     this.deferFocus();
44334                 }
44335                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44336                     this.cleanUpPaste.defer(100, this);
44337                     return;
44338                 }
44339                 
44340             };
44341         }else if(Roo.isSafari){
44342             return function(e){
44343                 var k = e.getKey();
44344                 
44345                 if(k == e.TAB){
44346                     e.stopEvent();
44347                     this.execCmd('InsertText','\t');
44348                     this.deferFocus();
44349                     return;
44350                 }
44351                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44352                     this.cleanUpPaste.defer(100, this);
44353                     return;
44354                 }
44355                 
44356              };
44357         }
44358     }(),
44359     
44360     getAllAncestors: function()
44361     {
44362         var p = this.getSelectedNode();
44363         var a = [];
44364         if (!p) {
44365             a.push(p); // push blank onto stack..
44366             p = this.getParentElement();
44367         }
44368         
44369         
44370         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44371             a.push(p);
44372             p = p.parentNode;
44373         }
44374         a.push(this.doc.body);
44375         return a;
44376     },
44377     lastSel : false,
44378     lastSelNode : false,
44379     
44380     
44381     getSelection : function() 
44382     {
44383         this.assignDocWin();
44384         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44385     },
44386     
44387     getSelectedNode: function() 
44388     {
44389         // this may only work on Gecko!!!
44390         
44391         // should we cache this!!!!
44392         
44393         
44394         
44395          
44396         var range = this.createRange(this.getSelection()).cloneRange();
44397         
44398         if (Roo.isIE) {
44399             var parent = range.parentElement();
44400             while (true) {
44401                 var testRange = range.duplicate();
44402                 testRange.moveToElementText(parent);
44403                 if (testRange.inRange(range)) {
44404                     break;
44405                 }
44406                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44407                     break;
44408                 }
44409                 parent = parent.parentElement;
44410             }
44411             return parent;
44412         }
44413         
44414         // is ancestor a text element.
44415         var ac =  range.commonAncestorContainer;
44416         if (ac.nodeType == 3) {
44417             ac = ac.parentNode;
44418         }
44419         
44420         var ar = ac.childNodes;
44421          
44422         var nodes = [];
44423         var other_nodes = [];
44424         var has_other_nodes = false;
44425         for (var i=0;i<ar.length;i++) {
44426             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44427                 continue;
44428             }
44429             // fullly contained node.
44430             
44431             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44432                 nodes.push(ar[i]);
44433                 continue;
44434             }
44435             
44436             // probably selected..
44437             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44438                 other_nodes.push(ar[i]);
44439                 continue;
44440             }
44441             // outer..
44442             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44443                 continue;
44444             }
44445             
44446             
44447             has_other_nodes = true;
44448         }
44449         if (!nodes.length && other_nodes.length) {
44450             nodes= other_nodes;
44451         }
44452         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44453             return false;
44454         }
44455         
44456         return nodes[0];
44457     },
44458     createRange: function(sel)
44459     {
44460         // this has strange effects when using with 
44461         // top toolbar - not sure if it's a great idea.
44462         //this.editor.contentWindow.focus();
44463         if (typeof sel != "undefined") {
44464             try {
44465                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44466             } catch(e) {
44467                 return this.doc.createRange();
44468             }
44469         } else {
44470             return this.doc.createRange();
44471         }
44472     },
44473     getParentElement: function()
44474     {
44475         
44476         this.assignDocWin();
44477         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44478         
44479         var range = this.createRange(sel);
44480          
44481         try {
44482             var p = range.commonAncestorContainer;
44483             while (p.nodeType == 3) { // text node
44484                 p = p.parentNode;
44485             }
44486             return p;
44487         } catch (e) {
44488             return null;
44489         }
44490     
44491     },
44492     /***
44493      *
44494      * Range intersection.. the hard stuff...
44495      *  '-1' = before
44496      *  '0' = hits..
44497      *  '1' = after.
44498      *         [ -- selected range --- ]
44499      *   [fail]                        [fail]
44500      *
44501      *    basically..
44502      *      if end is before start or  hits it. fail.
44503      *      if start is after end or hits it fail.
44504      *
44505      *   if either hits (but other is outside. - then it's not 
44506      *   
44507      *    
44508      **/
44509     
44510     
44511     // @see http://www.thismuchiknow.co.uk/?p=64.
44512     rangeIntersectsNode : function(range, node)
44513     {
44514         var nodeRange = node.ownerDocument.createRange();
44515         try {
44516             nodeRange.selectNode(node);
44517         } catch (e) {
44518             nodeRange.selectNodeContents(node);
44519         }
44520     
44521         var rangeStartRange = range.cloneRange();
44522         rangeStartRange.collapse(true);
44523     
44524         var rangeEndRange = range.cloneRange();
44525         rangeEndRange.collapse(false);
44526     
44527         var nodeStartRange = nodeRange.cloneRange();
44528         nodeStartRange.collapse(true);
44529     
44530         var nodeEndRange = nodeRange.cloneRange();
44531         nodeEndRange.collapse(false);
44532     
44533         return rangeStartRange.compareBoundaryPoints(
44534                  Range.START_TO_START, nodeEndRange) == -1 &&
44535                rangeEndRange.compareBoundaryPoints(
44536                  Range.START_TO_START, nodeStartRange) == 1;
44537         
44538          
44539     },
44540     rangeCompareNode : function(range, node)
44541     {
44542         var nodeRange = node.ownerDocument.createRange();
44543         try {
44544             nodeRange.selectNode(node);
44545         } catch (e) {
44546             nodeRange.selectNodeContents(node);
44547         }
44548         
44549         
44550         range.collapse(true);
44551     
44552         nodeRange.collapse(true);
44553      
44554         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44555         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44556          
44557         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44558         
44559         var nodeIsBefore   =  ss == 1;
44560         var nodeIsAfter    = ee == -1;
44561         
44562         if (nodeIsBefore && nodeIsAfter) {
44563             return 0; // outer
44564         }
44565         if (!nodeIsBefore && nodeIsAfter) {
44566             return 1; //right trailed.
44567         }
44568         
44569         if (nodeIsBefore && !nodeIsAfter) {
44570             return 2;  // left trailed.
44571         }
44572         // fully contined.
44573         return 3;
44574     },
44575
44576     // private? - in a new class?
44577     cleanUpPaste :  function()
44578     {
44579         // cleans up the whole document..
44580         Roo.log('cleanuppaste');
44581         
44582         this.cleanUpChildren(this.doc.body);
44583         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44584         if (clean != this.doc.body.innerHTML) {
44585             this.doc.body.innerHTML = clean;
44586         }
44587         
44588     },
44589     
44590     cleanWordChars : function(input) {// change the chars to hex code
44591         var he = Roo.HtmlEditorCore;
44592         
44593         var output = input;
44594         Roo.each(he.swapCodes, function(sw) { 
44595             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44596             
44597             output = output.replace(swapper, sw[1]);
44598         });
44599         
44600         return output;
44601     },
44602     
44603     
44604     cleanUpChildren : function (n)
44605     {
44606         if (!n.childNodes.length) {
44607             return;
44608         }
44609         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44610            this.cleanUpChild(n.childNodes[i]);
44611         }
44612     },
44613     
44614     
44615         
44616     
44617     cleanUpChild : function (node)
44618     {
44619         var ed = this;
44620         //console.log(node);
44621         if (node.nodeName == "#text") {
44622             // clean up silly Windows -- stuff?
44623             return; 
44624         }
44625         if (node.nodeName == "#comment") {
44626             node.parentNode.removeChild(node);
44627             // clean up silly Windows -- stuff?
44628             return; 
44629         }
44630         var lcname = node.tagName.toLowerCase();
44631         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44632         // whitelist of tags..
44633         
44634         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44635             // remove node.
44636             node.parentNode.removeChild(node);
44637             return;
44638             
44639         }
44640         
44641         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44642         
44643         // spans with no attributes - just remove them..
44644         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44645             remove_keep_children = true;
44646         }
44647         
44648         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44649         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44650         
44651         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44652         //    remove_keep_children = true;
44653         //}
44654         
44655         if (remove_keep_children) {
44656             this.cleanUpChildren(node);
44657             // inserts everything just before this node...
44658             while (node.childNodes.length) {
44659                 var cn = node.childNodes[0];
44660                 node.removeChild(cn);
44661                 node.parentNode.insertBefore(cn, node);
44662             }
44663             node.parentNode.removeChild(node);
44664             return;
44665         }
44666         
44667         if (!node.attributes || !node.attributes.length) {
44668             
44669           
44670             
44671             
44672             this.cleanUpChildren(node);
44673             return;
44674         }
44675         
44676         function cleanAttr(n,v)
44677         {
44678             
44679             if (v.match(/^\./) || v.match(/^\//)) {
44680                 return;
44681             }
44682             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44683                 return;
44684             }
44685             if (v.match(/^#/)) {
44686                 return;
44687             }
44688             if (v.match(/^\{/)) { // allow template editing.
44689                 return;
44690             }
44691 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44692             node.removeAttribute(n);
44693             
44694         }
44695         
44696         var cwhite = this.cwhite;
44697         var cblack = this.cblack;
44698             
44699         function cleanStyle(n,v)
44700         {
44701             if (v.match(/expression/)) { //XSS?? should we even bother..
44702                 node.removeAttribute(n);
44703                 return;
44704             }
44705             
44706             var parts = v.split(/;/);
44707             var clean = [];
44708             
44709             Roo.each(parts, function(p) {
44710                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44711                 if (!p.length) {
44712                     return true;
44713                 }
44714                 var l = p.split(':').shift().replace(/\s+/g,'');
44715                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44716                 
44717                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44718 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44719                     //node.removeAttribute(n);
44720                     return true;
44721                 }
44722                 //Roo.log()
44723                 // only allow 'c whitelisted system attributes'
44724                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44725 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44726                     //node.removeAttribute(n);
44727                     return true;
44728                 }
44729                 
44730                 
44731                  
44732                 
44733                 clean.push(p);
44734                 return true;
44735             });
44736             if (clean.length) { 
44737                 node.setAttribute(n, clean.join(';'));
44738             } else {
44739                 node.removeAttribute(n);
44740             }
44741             
44742         }
44743         
44744         
44745         for (var i = node.attributes.length-1; i > -1 ; i--) {
44746             var a = node.attributes[i];
44747             //console.log(a);
44748             
44749             if (a.name.toLowerCase().substr(0,2)=='on')  {
44750                 node.removeAttribute(a.name);
44751                 continue;
44752             }
44753             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44754                 node.removeAttribute(a.name);
44755                 continue;
44756             }
44757             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44758                 cleanAttr(a.name,a.value); // fixme..
44759                 continue;
44760             }
44761             if (a.name == 'style') {
44762                 cleanStyle(a.name,a.value);
44763                 continue;
44764             }
44765             /// clean up MS crap..
44766             // tecnically this should be a list of valid class'es..
44767             
44768             
44769             if (a.name == 'class') {
44770                 if (a.value.match(/^Mso/)) {
44771                     node.removeAttribute('class');
44772                 }
44773                 
44774                 if (a.value.match(/^body$/)) {
44775                     node.removeAttribute('class');
44776                 }
44777                 continue;
44778             }
44779             
44780             // style cleanup!?
44781             // class cleanup?
44782             
44783         }
44784         
44785         
44786         this.cleanUpChildren(node);
44787         
44788         
44789     },
44790     
44791     /**
44792      * Clean up MS wordisms...
44793      */
44794     cleanWord : function(node)
44795     {
44796         if (!node) {
44797             this.cleanWord(this.doc.body);
44798             return;
44799         }
44800         
44801         if(
44802                 node.nodeName == 'SPAN' &&
44803                 !node.hasAttributes() &&
44804                 node.childNodes.length == 1 &&
44805                 node.firstChild.nodeName == "#text"  
44806         ) {
44807             var textNode = node.firstChild;
44808             node.removeChild(textNode);
44809             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44810                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44811             }
44812             node.parentNode.insertBefore(textNode, node);
44813             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44814                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44815             }
44816             node.parentNode.removeChild(node);
44817         }
44818         
44819         if (node.nodeName == "#text") {
44820             // clean up silly Windows -- stuff?
44821             return; 
44822         }
44823         if (node.nodeName == "#comment") {
44824             node.parentNode.removeChild(node);
44825             // clean up silly Windows -- stuff?
44826             return; 
44827         }
44828         
44829         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44830             node.parentNode.removeChild(node);
44831             return;
44832         }
44833         //Roo.log(node.tagName);
44834         // remove - but keep children..
44835         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44836             //Roo.log('-- removed');
44837             while (node.childNodes.length) {
44838                 var cn = node.childNodes[0];
44839                 node.removeChild(cn);
44840                 node.parentNode.insertBefore(cn, node);
44841                 // move node to parent - and clean it..
44842                 this.cleanWord(cn);
44843             }
44844             node.parentNode.removeChild(node);
44845             /// no need to iterate chidlren = it's got none..
44846             //this.iterateChildren(node, this.cleanWord);
44847             return;
44848         }
44849         // clean styles
44850         if (node.className.length) {
44851             
44852             var cn = node.className.split(/\W+/);
44853             var cna = [];
44854             Roo.each(cn, function(cls) {
44855                 if (cls.match(/Mso[a-zA-Z]+/)) {
44856                     return;
44857                 }
44858                 cna.push(cls);
44859             });
44860             node.className = cna.length ? cna.join(' ') : '';
44861             if (!cna.length) {
44862                 node.removeAttribute("class");
44863             }
44864         }
44865         
44866         if (node.hasAttribute("lang")) {
44867             node.removeAttribute("lang");
44868         }
44869         
44870         if (node.hasAttribute("style")) {
44871             
44872             var styles = node.getAttribute("style").split(";");
44873             var nstyle = [];
44874             Roo.each(styles, function(s) {
44875                 if (!s.match(/:/)) {
44876                     return;
44877                 }
44878                 var kv = s.split(":");
44879                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44880                     return;
44881                 }
44882                 // what ever is left... we allow.
44883                 nstyle.push(s);
44884             });
44885             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44886             if (!nstyle.length) {
44887                 node.removeAttribute('style');
44888             }
44889         }
44890         this.iterateChildren(node, this.cleanWord);
44891         
44892         
44893         
44894     },
44895     /**
44896      * iterateChildren of a Node, calling fn each time, using this as the scole..
44897      * @param {DomNode} node node to iterate children of.
44898      * @param {Function} fn method of this class to call on each item.
44899      */
44900     iterateChildren : function(node, fn)
44901     {
44902         if (!node.childNodes.length) {
44903                 return;
44904         }
44905         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44906            fn.call(this, node.childNodes[i])
44907         }
44908     },
44909     
44910     
44911     /**
44912      * cleanTableWidths.
44913      *
44914      * Quite often pasting from word etc.. results in tables with column and widths.
44915      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44916      *
44917      */
44918     cleanTableWidths : function(node)
44919     {
44920          
44921          
44922         if (!node) {
44923             this.cleanTableWidths(this.doc.body);
44924             return;
44925         }
44926         
44927         // ignore list...
44928         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44929             return; 
44930         }
44931         Roo.log(node.tagName);
44932         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44933             this.iterateChildren(node, this.cleanTableWidths);
44934             return;
44935         }
44936         if (node.hasAttribute('width')) {
44937             node.removeAttribute('width');
44938         }
44939         
44940          
44941         if (node.hasAttribute("style")) {
44942             // pretty basic...
44943             
44944             var styles = node.getAttribute("style").split(";");
44945             var nstyle = [];
44946             Roo.each(styles, function(s) {
44947                 if (!s.match(/:/)) {
44948                     return;
44949                 }
44950                 var kv = s.split(":");
44951                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44952                     return;
44953                 }
44954                 // what ever is left... we allow.
44955                 nstyle.push(s);
44956             });
44957             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44958             if (!nstyle.length) {
44959                 node.removeAttribute('style');
44960             }
44961         }
44962         
44963         this.iterateChildren(node, this.cleanTableWidths);
44964         
44965         
44966     },
44967     
44968     
44969     
44970     
44971     domToHTML : function(currentElement, depth, nopadtext) {
44972         
44973         depth = depth || 0;
44974         nopadtext = nopadtext || false;
44975     
44976         if (!currentElement) {
44977             return this.domToHTML(this.doc.body);
44978         }
44979         
44980         //Roo.log(currentElement);
44981         var j;
44982         var allText = false;
44983         var nodeName = currentElement.nodeName;
44984         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
44985         
44986         if  (nodeName == '#text') {
44987             
44988             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
44989         }
44990         
44991         
44992         var ret = '';
44993         if (nodeName != 'BODY') {
44994              
44995             var i = 0;
44996             // Prints the node tagName, such as <A>, <IMG>, etc
44997             if (tagName) {
44998                 var attr = [];
44999                 for(i = 0; i < currentElement.attributes.length;i++) {
45000                     // quoting?
45001                     var aname = currentElement.attributes.item(i).name;
45002                     if (!currentElement.attributes.item(i).value.length) {
45003                         continue;
45004                     }
45005                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45006                 }
45007                 
45008                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45009             } 
45010             else {
45011                 
45012                 // eack
45013             }
45014         } else {
45015             tagName = false;
45016         }
45017         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45018             return ret;
45019         }
45020         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45021             nopadtext = true;
45022         }
45023         
45024         
45025         // Traverse the tree
45026         i = 0;
45027         var currentElementChild = currentElement.childNodes.item(i);
45028         var allText = true;
45029         var innerHTML  = '';
45030         lastnode = '';
45031         while (currentElementChild) {
45032             // Formatting code (indent the tree so it looks nice on the screen)
45033             var nopad = nopadtext;
45034             if (lastnode == 'SPAN') {
45035                 nopad  = true;
45036             }
45037             // text
45038             if  (currentElementChild.nodeName == '#text') {
45039                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45040                 toadd = nopadtext ? toadd : toadd.trim();
45041                 if (!nopad && toadd.length > 80) {
45042                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45043                 }
45044                 innerHTML  += toadd;
45045                 
45046                 i++;
45047                 currentElementChild = currentElement.childNodes.item(i);
45048                 lastNode = '';
45049                 continue;
45050             }
45051             allText = false;
45052             
45053             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45054                 
45055             // Recursively traverse the tree structure of the child node
45056             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45057             lastnode = currentElementChild.nodeName;
45058             i++;
45059             currentElementChild=currentElement.childNodes.item(i);
45060         }
45061         
45062         ret += innerHTML;
45063         
45064         if (!allText) {
45065                 // The remaining code is mostly for formatting the tree
45066             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45067         }
45068         
45069         
45070         if (tagName) {
45071             ret+= "</"+tagName+">";
45072         }
45073         return ret;
45074         
45075     },
45076         
45077     applyBlacklists : function()
45078     {
45079         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45080         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45081         
45082         this.white = [];
45083         this.black = [];
45084         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45085             if (b.indexOf(tag) > -1) {
45086                 return;
45087             }
45088             this.white.push(tag);
45089             
45090         }, this);
45091         
45092         Roo.each(w, function(tag) {
45093             if (b.indexOf(tag) > -1) {
45094                 return;
45095             }
45096             if (this.white.indexOf(tag) > -1) {
45097                 return;
45098             }
45099             this.white.push(tag);
45100             
45101         }, this);
45102         
45103         
45104         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45105             if (w.indexOf(tag) > -1) {
45106                 return;
45107             }
45108             this.black.push(tag);
45109             
45110         }, this);
45111         
45112         Roo.each(b, function(tag) {
45113             if (w.indexOf(tag) > -1) {
45114                 return;
45115             }
45116             if (this.black.indexOf(tag) > -1) {
45117                 return;
45118             }
45119             this.black.push(tag);
45120             
45121         }, this);
45122         
45123         
45124         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45125         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45126         
45127         this.cwhite = [];
45128         this.cblack = [];
45129         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45130             if (b.indexOf(tag) > -1) {
45131                 return;
45132             }
45133             this.cwhite.push(tag);
45134             
45135         }, this);
45136         
45137         Roo.each(w, function(tag) {
45138             if (b.indexOf(tag) > -1) {
45139                 return;
45140             }
45141             if (this.cwhite.indexOf(tag) > -1) {
45142                 return;
45143             }
45144             this.cwhite.push(tag);
45145             
45146         }, this);
45147         
45148         
45149         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45150             if (w.indexOf(tag) > -1) {
45151                 return;
45152             }
45153             this.cblack.push(tag);
45154             
45155         }, this);
45156         
45157         Roo.each(b, function(tag) {
45158             if (w.indexOf(tag) > -1) {
45159                 return;
45160             }
45161             if (this.cblack.indexOf(tag) > -1) {
45162                 return;
45163             }
45164             this.cblack.push(tag);
45165             
45166         }, this);
45167     },
45168     
45169     setStylesheets : function(stylesheets)
45170     {
45171         if(typeof(stylesheets) == 'string'){
45172             Roo.get(this.iframe.contentDocument.head).createChild({
45173                 tag : 'link',
45174                 rel : 'stylesheet',
45175                 type : 'text/css',
45176                 href : stylesheets
45177             });
45178             
45179             return;
45180         }
45181         var _this = this;
45182      
45183         Roo.each(stylesheets, function(s) {
45184             if(!s.length){
45185                 return;
45186             }
45187             
45188             Roo.get(_this.iframe.contentDocument.head).createChild({
45189                 tag : 'link',
45190                 rel : 'stylesheet',
45191                 type : 'text/css',
45192                 href : s
45193             });
45194         });
45195
45196         
45197     },
45198     
45199     removeStylesheets : function()
45200     {
45201         var _this = this;
45202         
45203         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45204             s.remove();
45205         });
45206     },
45207     
45208     setStyle : function(style)
45209     {
45210         Roo.get(this.iframe.contentDocument.head).createChild({
45211             tag : 'style',
45212             type : 'text/css',
45213             html : style
45214         });
45215
45216         return;
45217     }
45218     
45219     // hide stuff that is not compatible
45220     /**
45221      * @event blur
45222      * @hide
45223      */
45224     /**
45225      * @event change
45226      * @hide
45227      */
45228     /**
45229      * @event focus
45230      * @hide
45231      */
45232     /**
45233      * @event specialkey
45234      * @hide
45235      */
45236     /**
45237      * @cfg {String} fieldClass @hide
45238      */
45239     /**
45240      * @cfg {String} focusClass @hide
45241      */
45242     /**
45243      * @cfg {String} autoCreate @hide
45244      */
45245     /**
45246      * @cfg {String} inputType @hide
45247      */
45248     /**
45249      * @cfg {String} invalidClass @hide
45250      */
45251     /**
45252      * @cfg {String} invalidText @hide
45253      */
45254     /**
45255      * @cfg {String} msgFx @hide
45256      */
45257     /**
45258      * @cfg {String} validateOnBlur @hide
45259      */
45260 });
45261
45262 Roo.HtmlEditorCore.white = [
45263         'area', 'br', 'img', 'input', 'hr', 'wbr',
45264         
45265        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45266        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45267        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45268        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45269        'table',   'ul',         'xmp', 
45270        
45271        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45272       'thead',   'tr', 
45273      
45274       'dir', 'menu', 'ol', 'ul', 'dl',
45275        
45276       'embed',  'object'
45277 ];
45278
45279
45280 Roo.HtmlEditorCore.black = [
45281     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45282         'applet', // 
45283         'base',   'basefont', 'bgsound', 'blink',  'body', 
45284         'frame',  'frameset', 'head',    'html',   'ilayer', 
45285         'iframe', 'layer',  'link',     'meta',    'object',   
45286         'script', 'style' ,'title',  'xml' // clean later..
45287 ];
45288 Roo.HtmlEditorCore.clean = [
45289     'script', 'style', 'title', 'xml'
45290 ];
45291 Roo.HtmlEditorCore.remove = [
45292     'font'
45293 ];
45294 // attributes..
45295
45296 Roo.HtmlEditorCore.ablack = [
45297     'on'
45298 ];
45299     
45300 Roo.HtmlEditorCore.aclean = [ 
45301     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45302 ];
45303
45304 // protocols..
45305 Roo.HtmlEditorCore.pwhite= [
45306         'http',  'https',  'mailto'
45307 ];
45308
45309 // white listed style attributes.
45310 Roo.HtmlEditorCore.cwhite= [
45311       //  'text-align', /// default is to allow most things..
45312       
45313          
45314 //        'font-size'//??
45315 ];
45316
45317 // black listed style attributes.
45318 Roo.HtmlEditorCore.cblack= [
45319       //  'font-size' -- this can be set by the project 
45320 ];
45321
45322
45323 Roo.HtmlEditorCore.swapCodes   =[ 
45324     [    8211, "&#8211;" ], 
45325     [    8212, "&#8212;" ], 
45326     [    8216,  "'" ],  
45327     [    8217, "'" ],  
45328     [    8220, '"' ],  
45329     [    8221, '"' ],  
45330     [    8226, "*" ],  
45331     [    8230, "..." ]
45332 ]; 
45333
45334     //<script type="text/javascript">
45335
45336 /*
45337  * Ext JS Library 1.1.1
45338  * Copyright(c) 2006-2007, Ext JS, LLC.
45339  * Licence LGPL
45340  * 
45341  */
45342  
45343  
45344 Roo.form.HtmlEditor = function(config){
45345     
45346     
45347     
45348     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45349     
45350     if (!this.toolbars) {
45351         this.toolbars = [];
45352     }
45353     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45354     
45355     
45356 };
45357
45358 /**
45359  * @class Roo.form.HtmlEditor
45360  * @extends Roo.form.Field
45361  * Provides a lightweight HTML Editor component.
45362  *
45363  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45364  * 
45365  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45366  * supported by this editor.</b><br/><br/>
45367  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45368  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45369  */
45370 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45371     /**
45372      * @cfg {Boolean} clearUp
45373      */
45374     clearUp : true,
45375       /**
45376      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45377      */
45378     toolbars : false,
45379    
45380      /**
45381      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45382      *                        Roo.resizable.
45383      */
45384     resizable : false,
45385      /**
45386      * @cfg {Number} height (in pixels)
45387      */   
45388     height: 300,
45389    /**
45390      * @cfg {Number} width (in pixels)
45391      */   
45392     width: 500,
45393     
45394     /**
45395      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45396      * 
45397      */
45398     stylesheets: false,
45399     
45400     
45401      /**
45402      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45403      * 
45404      */
45405     cblack: false,
45406     /**
45407      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45408      * 
45409      */
45410     cwhite: false,
45411     
45412      /**
45413      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45414      * 
45415      */
45416     black: false,
45417     /**
45418      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45419      * 
45420      */
45421     white: false,
45422     
45423     // id of frame..
45424     frameId: false,
45425     
45426     // private properties
45427     validationEvent : false,
45428     deferHeight: true,
45429     initialized : false,
45430     activated : false,
45431     
45432     onFocus : Roo.emptyFn,
45433     iframePad:3,
45434     hideMode:'offsets',
45435     
45436     actionMode : 'container', // defaults to hiding it...
45437     
45438     defaultAutoCreate : { // modified by initCompnoent..
45439         tag: "textarea",
45440         style:"width:500px;height:300px;",
45441         autocomplete: "new-password"
45442     },
45443
45444     // private
45445     initComponent : function(){
45446         this.addEvents({
45447             /**
45448              * @event initialize
45449              * Fires when the editor is fully initialized (including the iframe)
45450              * @param {HtmlEditor} this
45451              */
45452             initialize: true,
45453             /**
45454              * @event activate
45455              * Fires when the editor is first receives the focus. Any insertion must wait
45456              * until after this event.
45457              * @param {HtmlEditor} this
45458              */
45459             activate: true,
45460              /**
45461              * @event beforesync
45462              * Fires before the textarea is updated with content from the editor iframe. Return false
45463              * to cancel the sync.
45464              * @param {HtmlEditor} this
45465              * @param {String} html
45466              */
45467             beforesync: true,
45468              /**
45469              * @event beforepush
45470              * Fires before the iframe editor is updated with content from the textarea. Return false
45471              * to cancel the push.
45472              * @param {HtmlEditor} this
45473              * @param {String} html
45474              */
45475             beforepush: true,
45476              /**
45477              * @event sync
45478              * Fires when the textarea is updated with content from the editor iframe.
45479              * @param {HtmlEditor} this
45480              * @param {String} html
45481              */
45482             sync: true,
45483              /**
45484              * @event push
45485              * Fires when the iframe editor is updated with content from the textarea.
45486              * @param {HtmlEditor} this
45487              * @param {String} html
45488              */
45489             push: true,
45490              /**
45491              * @event editmodechange
45492              * Fires when the editor switches edit modes
45493              * @param {HtmlEditor} this
45494              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45495              */
45496             editmodechange: true,
45497             /**
45498              * @event editorevent
45499              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45500              * @param {HtmlEditor} this
45501              */
45502             editorevent: true,
45503             /**
45504              * @event firstfocus
45505              * Fires when on first focus - needed by toolbars..
45506              * @param {HtmlEditor} this
45507              */
45508             firstfocus: true,
45509             /**
45510              * @event autosave
45511              * Auto save the htmlEditor value as a file into Events
45512              * @param {HtmlEditor} this
45513              */
45514             autosave: true,
45515             /**
45516              * @event savedpreview
45517              * preview the saved version of htmlEditor
45518              * @param {HtmlEditor} this
45519              */
45520             savedpreview: true,
45521             
45522             /**
45523             * @event stylesheetsclick
45524             * Fires when press the Sytlesheets button
45525             * @param {Roo.HtmlEditorCore} this
45526             */
45527             stylesheetsclick: true
45528         });
45529         this.defaultAutoCreate =  {
45530             tag: "textarea",
45531             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45532             autocomplete: "new-password"
45533         };
45534     },
45535
45536     /**
45537      * Protected method that will not generally be called directly. It
45538      * is called when the editor creates its toolbar. Override this method if you need to
45539      * add custom toolbar buttons.
45540      * @param {HtmlEditor} editor
45541      */
45542     createToolbar : function(editor){
45543         Roo.log("create toolbars");
45544         if (!editor.toolbars || !editor.toolbars.length) {
45545             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45546         }
45547         
45548         for (var i =0 ; i < editor.toolbars.length;i++) {
45549             editor.toolbars[i] = Roo.factory(
45550                     typeof(editor.toolbars[i]) == 'string' ?
45551                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45552                 Roo.form.HtmlEditor);
45553             editor.toolbars[i].init(editor);
45554         }
45555          
45556         
45557     },
45558
45559      
45560     // private
45561     onRender : function(ct, position)
45562     {
45563         var _t = this;
45564         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45565         
45566         this.wrap = this.el.wrap({
45567             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45568         });
45569         
45570         this.editorcore.onRender(ct, position);
45571          
45572         if (this.resizable) {
45573             this.resizeEl = new Roo.Resizable(this.wrap, {
45574                 pinned : true,
45575                 wrap: true,
45576                 dynamic : true,
45577                 minHeight : this.height,
45578                 height: this.height,
45579                 handles : this.resizable,
45580                 width: this.width,
45581                 listeners : {
45582                     resize : function(r, w, h) {
45583                         _t.onResize(w,h); // -something
45584                     }
45585                 }
45586             });
45587             
45588         }
45589         this.createToolbar(this);
45590        
45591         
45592         if(!this.width){
45593             this.setSize(this.wrap.getSize());
45594         }
45595         if (this.resizeEl) {
45596             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45597             // should trigger onReize..
45598         }
45599         
45600         this.keyNav = new Roo.KeyNav(this.el, {
45601             
45602             "tab" : function(e){
45603                 e.preventDefault();
45604                 
45605                 var value = this.getValue();
45606                 
45607                 var start = this.el.dom.selectionStart;
45608                 var end = this.el.dom.selectionEnd;
45609                 
45610                 if(!e.shiftKey){
45611                     
45612                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45613                     this.el.dom.setSelectionRange(end + 1, end + 1);
45614                     return;
45615                 }
45616                 
45617                 var f = value.substring(0, start).split("\t");
45618                 
45619                 if(f.pop().length != 0){
45620                     return;
45621                 }
45622                 
45623                 this.setValue(f.join("\t") + value.substring(end));
45624                 this.el.dom.setSelectionRange(start - 1, start - 1);
45625                 
45626             },
45627             
45628             "home" : function(e){
45629                 e.preventDefault();
45630                 
45631                 var curr = this.el.dom.selectionStart;
45632                 var lines = this.getValue().split("\n");
45633                 
45634                 if(!lines.length){
45635                     return;
45636                 }
45637                 
45638                 if(e.ctrlKey){
45639                     this.el.dom.setSelectionRange(0, 0);
45640                     return;
45641                 }
45642                 
45643                 var pos = 0;
45644                 
45645                 for (var i = 0; i < lines.length;i++) {
45646                     pos += lines[i].length;
45647                     
45648                     if(i != 0){
45649                         pos += 1;
45650                     }
45651                     
45652                     if(pos < curr){
45653                         continue;
45654                     }
45655                     
45656                     pos -= lines[i].length;
45657                     
45658                     break;
45659                 }
45660                 
45661                 if(!e.shiftKey){
45662                     this.el.dom.setSelectionRange(pos, pos);
45663                     return;
45664                 }
45665                 
45666                 this.el.dom.selectionStart = pos;
45667                 this.el.dom.selectionEnd = curr;
45668             },
45669             
45670             "end" : function(e){
45671                 e.preventDefault();
45672                 
45673                 var curr = this.el.dom.selectionStart;
45674                 var lines = this.getValue().split("\n");
45675                 
45676                 if(!lines.length){
45677                     return;
45678                 }
45679                 
45680                 if(e.ctrlKey){
45681                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45682                     return;
45683                 }
45684                 
45685                 var pos = 0;
45686                 
45687                 for (var i = 0; i < lines.length;i++) {
45688                     
45689                     pos += lines[i].length;
45690                     
45691                     if(i != 0){
45692                         pos += 1;
45693                     }
45694                     
45695                     if(pos < curr){
45696                         continue;
45697                     }
45698                     
45699                     break;
45700                 }
45701                 
45702                 if(!e.shiftKey){
45703                     this.el.dom.setSelectionRange(pos, pos);
45704                     return;
45705                 }
45706                 
45707                 this.el.dom.selectionStart = curr;
45708                 this.el.dom.selectionEnd = pos;
45709             },
45710
45711             scope : this,
45712
45713             doRelay : function(foo, bar, hname){
45714                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45715             },
45716
45717             forceKeyDown: true
45718         });
45719         
45720 //        if(this.autosave && this.w){
45721 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45722 //        }
45723     },
45724
45725     // private
45726     onResize : function(w, h)
45727     {
45728         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45729         var ew = false;
45730         var eh = false;
45731         
45732         if(this.el ){
45733             if(typeof w == 'number'){
45734                 var aw = w - this.wrap.getFrameWidth('lr');
45735                 this.el.setWidth(this.adjustWidth('textarea', aw));
45736                 ew = aw;
45737             }
45738             if(typeof h == 'number'){
45739                 var tbh = 0;
45740                 for (var i =0; i < this.toolbars.length;i++) {
45741                     // fixme - ask toolbars for heights?
45742                     tbh += this.toolbars[i].tb.el.getHeight();
45743                     if (this.toolbars[i].footer) {
45744                         tbh += this.toolbars[i].footer.el.getHeight();
45745                     }
45746                 }
45747                 
45748                 
45749                 
45750                 
45751                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45752                 ah -= 5; // knock a few pixes off for look..
45753 //                Roo.log(ah);
45754                 this.el.setHeight(this.adjustWidth('textarea', ah));
45755                 var eh = ah;
45756             }
45757         }
45758         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45759         this.editorcore.onResize(ew,eh);
45760         
45761     },
45762
45763     /**
45764      * Toggles the editor between standard and source edit mode.
45765      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45766      */
45767     toggleSourceEdit : function(sourceEditMode)
45768     {
45769         this.editorcore.toggleSourceEdit(sourceEditMode);
45770         
45771         if(this.editorcore.sourceEditMode){
45772             Roo.log('editor - showing textarea');
45773             
45774 //            Roo.log('in');
45775 //            Roo.log(this.syncValue());
45776             this.editorcore.syncValue();
45777             this.el.removeClass('x-hidden');
45778             this.el.dom.removeAttribute('tabIndex');
45779             this.el.focus();
45780             
45781             for (var i = 0; i < this.toolbars.length; i++) {
45782                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45783                     this.toolbars[i].tb.hide();
45784                     this.toolbars[i].footer.hide();
45785                 }
45786             }
45787             
45788         }else{
45789             Roo.log('editor - hiding textarea');
45790 //            Roo.log('out')
45791 //            Roo.log(this.pushValue()); 
45792             this.editorcore.pushValue();
45793             
45794             this.el.addClass('x-hidden');
45795             this.el.dom.setAttribute('tabIndex', -1);
45796             
45797             for (var i = 0; i < this.toolbars.length; i++) {
45798                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45799                     this.toolbars[i].tb.show();
45800                     this.toolbars[i].footer.show();
45801                 }
45802             }
45803             
45804             //this.deferFocus();
45805         }
45806         
45807         this.setSize(this.wrap.getSize());
45808         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45809         
45810         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45811     },
45812  
45813     // private (for BoxComponent)
45814     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45815
45816     // private (for BoxComponent)
45817     getResizeEl : function(){
45818         return this.wrap;
45819     },
45820
45821     // private (for BoxComponent)
45822     getPositionEl : function(){
45823         return this.wrap;
45824     },
45825
45826     // private
45827     initEvents : function(){
45828         this.originalValue = this.getValue();
45829     },
45830
45831     /**
45832      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45833      * @method
45834      */
45835     markInvalid : Roo.emptyFn,
45836     /**
45837      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45838      * @method
45839      */
45840     clearInvalid : Roo.emptyFn,
45841
45842     setValue : function(v){
45843         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45844         this.editorcore.pushValue();
45845     },
45846
45847      
45848     // private
45849     deferFocus : function(){
45850         this.focus.defer(10, this);
45851     },
45852
45853     // doc'ed in Field
45854     focus : function(){
45855         this.editorcore.focus();
45856         
45857     },
45858       
45859
45860     // private
45861     onDestroy : function(){
45862         
45863         
45864         
45865         if(this.rendered){
45866             
45867             for (var i =0; i < this.toolbars.length;i++) {
45868                 // fixme - ask toolbars for heights?
45869                 this.toolbars[i].onDestroy();
45870             }
45871             
45872             this.wrap.dom.innerHTML = '';
45873             this.wrap.remove();
45874         }
45875     },
45876
45877     // private
45878     onFirstFocus : function(){
45879         //Roo.log("onFirstFocus");
45880         this.editorcore.onFirstFocus();
45881          for (var i =0; i < this.toolbars.length;i++) {
45882             this.toolbars[i].onFirstFocus();
45883         }
45884         
45885     },
45886     
45887     // private
45888     syncValue : function()
45889     {
45890         this.editorcore.syncValue();
45891     },
45892     
45893     pushValue : function()
45894     {
45895         this.editorcore.pushValue();
45896     },
45897     
45898     setStylesheets : function(stylesheets)
45899     {
45900         this.editorcore.setStylesheets(stylesheets);
45901     },
45902     
45903     removeStylesheets : function()
45904     {
45905         this.editorcore.removeStylesheets();
45906     }
45907      
45908     
45909     // hide stuff that is not compatible
45910     /**
45911      * @event blur
45912      * @hide
45913      */
45914     /**
45915      * @event change
45916      * @hide
45917      */
45918     /**
45919      * @event focus
45920      * @hide
45921      */
45922     /**
45923      * @event specialkey
45924      * @hide
45925      */
45926     /**
45927      * @cfg {String} fieldClass @hide
45928      */
45929     /**
45930      * @cfg {String} focusClass @hide
45931      */
45932     /**
45933      * @cfg {String} autoCreate @hide
45934      */
45935     /**
45936      * @cfg {String} inputType @hide
45937      */
45938     /**
45939      * @cfg {String} invalidClass @hide
45940      */
45941     /**
45942      * @cfg {String} invalidText @hide
45943      */
45944     /**
45945      * @cfg {String} msgFx @hide
45946      */
45947     /**
45948      * @cfg {String} validateOnBlur @hide
45949      */
45950 });
45951  
45952     // <script type="text/javascript">
45953 /*
45954  * Based on
45955  * Ext JS Library 1.1.1
45956  * Copyright(c) 2006-2007, Ext JS, LLC.
45957  *  
45958  
45959  */
45960
45961 /**
45962  * @class Roo.form.HtmlEditorToolbar1
45963  * Basic Toolbar
45964  * 
45965  * Usage:
45966  *
45967  new Roo.form.HtmlEditor({
45968     ....
45969     toolbars : [
45970         new Roo.form.HtmlEditorToolbar1({
45971             disable : { fonts: 1 , format: 1, ..., ... , ...],
45972             btns : [ .... ]
45973         })
45974     }
45975      
45976  * 
45977  * @cfg {Object} disable List of elements to disable..
45978  * @cfg {Array} btns List of additional buttons.
45979  * 
45980  * 
45981  * NEEDS Extra CSS? 
45982  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
45983  */
45984  
45985 Roo.form.HtmlEditor.ToolbarStandard = function(config)
45986 {
45987     
45988     Roo.apply(this, config);
45989     
45990     // default disabled, based on 'good practice'..
45991     this.disable = this.disable || {};
45992     Roo.applyIf(this.disable, {
45993         fontSize : true,
45994         colors : true,
45995         specialElements : true
45996     });
45997     
45998     
45999     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46000     // dont call parent... till later.
46001 }
46002
46003 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46004     
46005     tb: false,
46006     
46007     rendered: false,
46008     
46009     editor : false,
46010     editorcore : false,
46011     /**
46012      * @cfg {Object} disable  List of toolbar elements to disable
46013          
46014      */
46015     disable : false,
46016     
46017     
46018      /**
46019      * @cfg {String} createLinkText The default text for the create link prompt
46020      */
46021     createLinkText : 'Please enter the URL for the link:',
46022     /**
46023      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46024      */
46025     defaultLinkValue : 'http:/'+'/',
46026    
46027     
46028       /**
46029      * @cfg {Array} fontFamilies An array of available font families
46030      */
46031     fontFamilies : [
46032         'Arial',
46033         'Courier New',
46034         'Tahoma',
46035         'Times New Roman',
46036         'Verdana'
46037     ],
46038     
46039     specialChars : [
46040            "&#169;",
46041           "&#174;",     
46042           "&#8482;",    
46043           "&#163;" ,    
46044          // "&#8212;",    
46045           "&#8230;",    
46046           "&#247;" ,    
46047         //  "&#225;" ,     ?? a acute?
46048            "&#8364;"    , //Euro
46049        //   "&#8220;"    ,
46050         //  "&#8221;"    ,
46051         //  "&#8226;"    ,
46052           "&#176;"  //   , // degrees
46053
46054          // "&#233;"     , // e ecute
46055          // "&#250;"     , // u ecute?
46056     ],
46057     
46058     specialElements : [
46059         {
46060             text: "Insert Table",
46061             xtype: 'MenuItem',
46062             xns : Roo.Menu,
46063             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46064                 
46065         },
46066         {    
46067             text: "Insert Image",
46068             xtype: 'MenuItem',
46069             xns : Roo.Menu,
46070             ihtml : '<img src="about:blank"/>'
46071             
46072         }
46073         
46074          
46075     ],
46076     
46077     
46078     inputElements : [ 
46079             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46080             "input:submit", "input:button", "select", "textarea", "label" ],
46081     formats : [
46082         ["p"] ,  
46083         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46084         ["pre"],[ "code"], 
46085         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46086         ['div'],['span'],
46087         ['sup'],['sub']
46088     ],
46089     
46090     cleanStyles : [
46091         "font-size"
46092     ],
46093      /**
46094      * @cfg {String} defaultFont default font to use.
46095      */
46096     defaultFont: 'tahoma',
46097    
46098     fontSelect : false,
46099     
46100     
46101     formatCombo : false,
46102     
46103     init : function(editor)
46104     {
46105         this.editor = editor;
46106         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46107         var editorcore = this.editorcore;
46108         
46109         var _t = this;
46110         
46111         var fid = editorcore.frameId;
46112         var etb = this;
46113         function btn(id, toggle, handler){
46114             var xid = fid + '-'+ id ;
46115             return {
46116                 id : xid,
46117                 cmd : id,
46118                 cls : 'x-btn-icon x-edit-'+id,
46119                 enableToggle:toggle !== false,
46120                 scope: _t, // was editor...
46121                 handler:handler||_t.relayBtnCmd,
46122                 clickEvent:'mousedown',
46123                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46124                 tabIndex:-1
46125             };
46126         }
46127         
46128         
46129         
46130         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46131         this.tb = tb;
46132          // stop form submits
46133         tb.el.on('click', function(e){
46134             e.preventDefault(); // what does this do?
46135         });
46136
46137         if(!this.disable.font) { // && !Roo.isSafari){
46138             /* why no safari for fonts 
46139             editor.fontSelect = tb.el.createChild({
46140                 tag:'select',
46141                 tabIndex: -1,
46142                 cls:'x-font-select',
46143                 html: this.createFontOptions()
46144             });
46145             
46146             editor.fontSelect.on('change', function(){
46147                 var font = editor.fontSelect.dom.value;
46148                 editor.relayCmd('fontname', font);
46149                 editor.deferFocus();
46150             }, editor);
46151             
46152             tb.add(
46153                 editor.fontSelect.dom,
46154                 '-'
46155             );
46156             */
46157             
46158         };
46159         if(!this.disable.formats){
46160             this.formatCombo = new Roo.form.ComboBox({
46161                 store: new Roo.data.SimpleStore({
46162                     id : 'tag',
46163                     fields: ['tag'],
46164                     data : this.formats // from states.js
46165                 }),
46166                 blockFocus : true,
46167                 name : '',
46168                 //autoCreate : {tag: "div",  size: "20"},
46169                 displayField:'tag',
46170                 typeAhead: false,
46171                 mode: 'local',
46172                 editable : false,
46173                 triggerAction: 'all',
46174                 emptyText:'Add tag',
46175                 selectOnFocus:true,
46176                 width:135,
46177                 listeners : {
46178                     'select': function(c, r, i) {
46179                         editorcore.insertTag(r.get('tag'));
46180                         editor.focus();
46181                     }
46182                 }
46183
46184             });
46185             tb.addField(this.formatCombo);
46186             
46187         }
46188         
46189         if(!this.disable.format){
46190             tb.add(
46191                 btn('bold'),
46192                 btn('italic'),
46193                 btn('underline'),
46194                 btn('strikethrough')
46195             );
46196         };
46197         if(!this.disable.fontSize){
46198             tb.add(
46199                 '-',
46200                 
46201                 
46202                 btn('increasefontsize', false, editorcore.adjustFont),
46203                 btn('decreasefontsize', false, editorcore.adjustFont)
46204             );
46205         };
46206         
46207         
46208         if(!this.disable.colors){
46209             tb.add(
46210                 '-', {
46211                     id:editorcore.frameId +'-forecolor',
46212                     cls:'x-btn-icon x-edit-forecolor',
46213                     clickEvent:'mousedown',
46214                     tooltip: this.buttonTips['forecolor'] || undefined,
46215                     tabIndex:-1,
46216                     menu : new Roo.menu.ColorMenu({
46217                         allowReselect: true,
46218                         focus: Roo.emptyFn,
46219                         value:'000000',
46220                         plain:true,
46221                         selectHandler: function(cp, color){
46222                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46223                             editor.deferFocus();
46224                         },
46225                         scope: editorcore,
46226                         clickEvent:'mousedown'
46227                     })
46228                 }, {
46229                     id:editorcore.frameId +'backcolor',
46230                     cls:'x-btn-icon x-edit-backcolor',
46231                     clickEvent:'mousedown',
46232                     tooltip: this.buttonTips['backcolor'] || undefined,
46233                     tabIndex:-1,
46234                     menu : new Roo.menu.ColorMenu({
46235                         focus: Roo.emptyFn,
46236                         value:'FFFFFF',
46237                         plain:true,
46238                         allowReselect: true,
46239                         selectHandler: function(cp, color){
46240                             if(Roo.isGecko){
46241                                 editorcore.execCmd('useCSS', false);
46242                                 editorcore.execCmd('hilitecolor', color);
46243                                 editorcore.execCmd('useCSS', true);
46244                                 editor.deferFocus();
46245                             }else{
46246                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46247                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46248                                 editor.deferFocus();
46249                             }
46250                         },
46251                         scope:editorcore,
46252                         clickEvent:'mousedown'
46253                     })
46254                 }
46255             );
46256         };
46257         // now add all the items...
46258         
46259
46260         if(!this.disable.alignments){
46261             tb.add(
46262                 '-',
46263                 btn('justifyleft'),
46264                 btn('justifycenter'),
46265                 btn('justifyright')
46266             );
46267         };
46268
46269         //if(!Roo.isSafari){
46270             if(!this.disable.links){
46271                 tb.add(
46272                     '-',
46273                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46274                 );
46275             };
46276
46277             if(!this.disable.lists){
46278                 tb.add(
46279                     '-',
46280                     btn('insertorderedlist'),
46281                     btn('insertunorderedlist')
46282                 );
46283             }
46284             if(!this.disable.sourceEdit){
46285                 tb.add(
46286                     '-',
46287                     btn('sourceedit', true, function(btn){
46288                         this.toggleSourceEdit(btn.pressed);
46289                     })
46290                 );
46291             }
46292         //}
46293         
46294         var smenu = { };
46295         // special menu.. - needs to be tidied up..
46296         if (!this.disable.special) {
46297             smenu = {
46298                 text: "&#169;",
46299                 cls: 'x-edit-none',
46300                 
46301                 menu : {
46302                     items : []
46303                 }
46304             };
46305             for (var i =0; i < this.specialChars.length; i++) {
46306                 smenu.menu.items.push({
46307                     
46308                     html: this.specialChars[i],
46309                     handler: function(a,b) {
46310                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46311                         //editor.insertAtCursor(a.html);
46312                         
46313                     },
46314                     tabIndex:-1
46315                 });
46316             }
46317             
46318             
46319             tb.add(smenu);
46320             
46321             
46322         }
46323         
46324         var cmenu = { };
46325         if (!this.disable.cleanStyles) {
46326             cmenu = {
46327                 cls: 'x-btn-icon x-btn-clear',
46328                 
46329                 menu : {
46330                     items : []
46331                 }
46332             };
46333             for (var i =0; i < this.cleanStyles.length; i++) {
46334                 cmenu.menu.items.push({
46335                     actiontype : this.cleanStyles[i],
46336                     html: 'Remove ' + this.cleanStyles[i],
46337                     handler: function(a,b) {
46338 //                        Roo.log(a);
46339 //                        Roo.log(b);
46340                         var c = Roo.get(editorcore.doc.body);
46341                         c.select('[style]').each(function(s) {
46342                             s.dom.style.removeProperty(a.actiontype);
46343                         });
46344                         editorcore.syncValue();
46345                     },
46346                     tabIndex:-1
46347                 });
46348             }
46349              cmenu.menu.items.push({
46350                 actiontype : 'tablewidths',
46351                 html: 'Remove Table Widths',
46352                 handler: function(a,b) {
46353                     editorcore.cleanTableWidths();
46354                     editorcore.syncValue();
46355                 },
46356                 tabIndex:-1
46357             });
46358             cmenu.menu.items.push({
46359                 actiontype : 'word',
46360                 html: 'Remove MS Word Formating',
46361                 handler: function(a,b) {
46362                     editorcore.cleanWord();
46363                     editorcore.syncValue();
46364                 },
46365                 tabIndex:-1
46366             });
46367             
46368             cmenu.menu.items.push({
46369                 actiontype : 'all',
46370                 html: 'Remove All Styles',
46371                 handler: function(a,b) {
46372                     
46373                     var c = Roo.get(editorcore.doc.body);
46374                     c.select('[style]').each(function(s) {
46375                         s.dom.removeAttribute('style');
46376                     });
46377                     editorcore.syncValue();
46378                 },
46379                 tabIndex:-1
46380             });
46381             
46382             cmenu.menu.items.push({
46383                 actiontype : 'all',
46384                 html: 'Remove All CSS Classes',
46385                 handler: function(a,b) {
46386                     
46387                     var c = Roo.get(editorcore.doc.body);
46388                     c.select('[class]').each(function(s) {
46389                         s.dom.removeAttribute('class');
46390                     });
46391                     editorcore.cleanWord();
46392                     editorcore.syncValue();
46393                 },
46394                 tabIndex:-1
46395             });
46396             
46397              cmenu.menu.items.push({
46398                 actiontype : 'tidy',
46399                 html: 'Tidy HTML Source',
46400                 handler: function(a,b) {
46401                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46402                     editorcore.syncValue();
46403                 },
46404                 tabIndex:-1
46405             });
46406             
46407             
46408             tb.add(cmenu);
46409         }
46410          
46411         if (!this.disable.specialElements) {
46412             var semenu = {
46413                 text: "Other;",
46414                 cls: 'x-edit-none',
46415                 menu : {
46416                     items : []
46417                 }
46418             };
46419             for (var i =0; i < this.specialElements.length; i++) {
46420                 semenu.menu.items.push(
46421                     Roo.apply({ 
46422                         handler: function(a,b) {
46423                             editor.insertAtCursor(this.ihtml);
46424                         }
46425                     }, this.specialElements[i])
46426                 );
46427                     
46428             }
46429             
46430             tb.add(semenu);
46431             
46432             
46433         }
46434          
46435         
46436         if (this.btns) {
46437             for(var i =0; i< this.btns.length;i++) {
46438                 var b = Roo.factory(this.btns[i],Roo.form);
46439                 b.cls =  'x-edit-none';
46440                 
46441                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46442                     b.cls += ' x-init-enable';
46443                 }
46444                 
46445                 b.scope = editorcore;
46446                 tb.add(b);
46447             }
46448         
46449         }
46450         
46451         
46452         
46453         // disable everything...
46454         
46455         this.tb.items.each(function(item){
46456             
46457            if(
46458                 item.id != editorcore.frameId+ '-sourceedit' && 
46459                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46460             ){
46461                 
46462                 item.disable();
46463             }
46464         });
46465         this.rendered = true;
46466         
46467         // the all the btns;
46468         editor.on('editorevent', this.updateToolbar, this);
46469         // other toolbars need to implement this..
46470         //editor.on('editmodechange', this.updateToolbar, this);
46471     },
46472     
46473     
46474     relayBtnCmd : function(btn) {
46475         this.editorcore.relayCmd(btn.cmd);
46476     },
46477     // private used internally
46478     createLink : function(){
46479         Roo.log("create link?");
46480         var url = prompt(this.createLinkText, this.defaultLinkValue);
46481         if(url && url != 'http:/'+'/'){
46482             this.editorcore.relayCmd('createlink', url);
46483         }
46484     },
46485
46486     
46487     /**
46488      * Protected method that will not generally be called directly. It triggers
46489      * a toolbar update by reading the markup state of the current selection in the editor.
46490      */
46491     updateToolbar: function(){
46492
46493         if(!this.editorcore.activated){
46494             this.editor.onFirstFocus();
46495             return;
46496         }
46497
46498         var btns = this.tb.items.map, 
46499             doc = this.editorcore.doc,
46500             frameId = this.editorcore.frameId;
46501
46502         if(!this.disable.font && !Roo.isSafari){
46503             /*
46504             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46505             if(name != this.fontSelect.dom.value){
46506                 this.fontSelect.dom.value = name;
46507             }
46508             */
46509         }
46510         if(!this.disable.format){
46511             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46512             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46513             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46514             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46515         }
46516         if(!this.disable.alignments){
46517             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46518             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46519             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46520         }
46521         if(!Roo.isSafari && !this.disable.lists){
46522             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46523             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46524         }
46525         
46526         var ans = this.editorcore.getAllAncestors();
46527         if (this.formatCombo) {
46528             
46529             
46530             var store = this.formatCombo.store;
46531             this.formatCombo.setValue("");
46532             for (var i =0; i < ans.length;i++) {
46533                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46534                     // select it..
46535                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46536                     break;
46537                 }
46538             }
46539         }
46540         
46541         
46542         
46543         // hides menus... - so this cant be on a menu...
46544         Roo.menu.MenuMgr.hideAll();
46545
46546         //this.editorsyncValue();
46547     },
46548    
46549     
46550     createFontOptions : function(){
46551         var buf = [], fs = this.fontFamilies, ff, lc;
46552         
46553         
46554         
46555         for(var i = 0, len = fs.length; i< len; i++){
46556             ff = fs[i];
46557             lc = ff.toLowerCase();
46558             buf.push(
46559                 '<option value="',lc,'" style="font-family:',ff,';"',
46560                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46561                     ff,
46562                 '</option>'
46563             );
46564         }
46565         return buf.join('');
46566     },
46567     
46568     toggleSourceEdit : function(sourceEditMode){
46569         
46570         Roo.log("toolbar toogle");
46571         if(sourceEditMode === undefined){
46572             sourceEditMode = !this.sourceEditMode;
46573         }
46574         this.sourceEditMode = sourceEditMode === true;
46575         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46576         // just toggle the button?
46577         if(btn.pressed !== this.sourceEditMode){
46578             btn.toggle(this.sourceEditMode);
46579             return;
46580         }
46581         
46582         if(sourceEditMode){
46583             Roo.log("disabling buttons");
46584             this.tb.items.each(function(item){
46585                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46586                     item.disable();
46587                 }
46588             });
46589           
46590         }else{
46591             Roo.log("enabling buttons");
46592             if(this.editorcore.initialized){
46593                 this.tb.items.each(function(item){
46594                     item.enable();
46595                 });
46596             }
46597             
46598         }
46599         Roo.log("calling toggole on editor");
46600         // tell the editor that it's been pressed..
46601         this.editor.toggleSourceEdit(sourceEditMode);
46602        
46603     },
46604      /**
46605      * Object collection of toolbar tooltips for the buttons in the editor. The key
46606      * is the command id associated with that button and the value is a valid QuickTips object.
46607      * For example:
46608 <pre><code>
46609 {
46610     bold : {
46611         title: 'Bold (Ctrl+B)',
46612         text: 'Make the selected text bold.',
46613         cls: 'x-html-editor-tip'
46614     },
46615     italic : {
46616         title: 'Italic (Ctrl+I)',
46617         text: 'Make the selected text italic.',
46618         cls: 'x-html-editor-tip'
46619     },
46620     ...
46621 </code></pre>
46622     * @type Object
46623      */
46624     buttonTips : {
46625         bold : {
46626             title: 'Bold (Ctrl+B)',
46627             text: 'Make the selected text bold.',
46628             cls: 'x-html-editor-tip'
46629         },
46630         italic : {
46631             title: 'Italic (Ctrl+I)',
46632             text: 'Make the selected text italic.',
46633             cls: 'x-html-editor-tip'
46634         },
46635         underline : {
46636             title: 'Underline (Ctrl+U)',
46637             text: 'Underline the selected text.',
46638             cls: 'x-html-editor-tip'
46639         },
46640         strikethrough : {
46641             title: 'Strikethrough',
46642             text: 'Strikethrough the selected text.',
46643             cls: 'x-html-editor-tip'
46644         },
46645         increasefontsize : {
46646             title: 'Grow Text',
46647             text: 'Increase the font size.',
46648             cls: 'x-html-editor-tip'
46649         },
46650         decreasefontsize : {
46651             title: 'Shrink Text',
46652             text: 'Decrease the font size.',
46653             cls: 'x-html-editor-tip'
46654         },
46655         backcolor : {
46656             title: 'Text Highlight Color',
46657             text: 'Change the background color of the selected text.',
46658             cls: 'x-html-editor-tip'
46659         },
46660         forecolor : {
46661             title: 'Font Color',
46662             text: 'Change the color of the selected text.',
46663             cls: 'x-html-editor-tip'
46664         },
46665         justifyleft : {
46666             title: 'Align Text Left',
46667             text: 'Align text to the left.',
46668             cls: 'x-html-editor-tip'
46669         },
46670         justifycenter : {
46671             title: 'Center Text',
46672             text: 'Center text in the editor.',
46673             cls: 'x-html-editor-tip'
46674         },
46675         justifyright : {
46676             title: 'Align Text Right',
46677             text: 'Align text to the right.',
46678             cls: 'x-html-editor-tip'
46679         },
46680         insertunorderedlist : {
46681             title: 'Bullet List',
46682             text: 'Start a bulleted list.',
46683             cls: 'x-html-editor-tip'
46684         },
46685         insertorderedlist : {
46686             title: 'Numbered List',
46687             text: 'Start a numbered list.',
46688             cls: 'x-html-editor-tip'
46689         },
46690         createlink : {
46691             title: 'Hyperlink',
46692             text: 'Make the selected text a hyperlink.',
46693             cls: 'x-html-editor-tip'
46694         },
46695         sourceedit : {
46696             title: 'Source Edit',
46697             text: 'Switch to source editing mode.',
46698             cls: 'x-html-editor-tip'
46699         }
46700     },
46701     // private
46702     onDestroy : function(){
46703         if(this.rendered){
46704             
46705             this.tb.items.each(function(item){
46706                 if(item.menu){
46707                     item.menu.removeAll();
46708                     if(item.menu.el){
46709                         item.menu.el.destroy();
46710                     }
46711                 }
46712                 item.destroy();
46713             });
46714              
46715         }
46716     },
46717     onFirstFocus: function() {
46718         this.tb.items.each(function(item){
46719            item.enable();
46720         });
46721     }
46722 });
46723
46724
46725
46726
46727 // <script type="text/javascript">
46728 /*
46729  * Based on
46730  * Ext JS Library 1.1.1
46731  * Copyright(c) 2006-2007, Ext JS, LLC.
46732  *  
46733  
46734  */
46735
46736  
46737 /**
46738  * @class Roo.form.HtmlEditor.ToolbarContext
46739  * Context Toolbar
46740  * 
46741  * Usage:
46742  *
46743  new Roo.form.HtmlEditor({
46744     ....
46745     toolbars : [
46746         { xtype: 'ToolbarStandard', styles : {} }
46747         { xtype: 'ToolbarContext', disable : {} }
46748     ]
46749 })
46750
46751      
46752  * 
46753  * @config : {Object} disable List of elements to disable.. (not done yet.)
46754  * @config : {Object} styles  Map of styles available.
46755  * 
46756  */
46757
46758 Roo.form.HtmlEditor.ToolbarContext = function(config)
46759 {
46760     
46761     Roo.apply(this, config);
46762     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46763     // dont call parent... till later.
46764     this.styles = this.styles || {};
46765 }
46766
46767  
46768
46769 Roo.form.HtmlEditor.ToolbarContext.types = {
46770     'IMG' : {
46771         width : {
46772             title: "Width",
46773             width: 40
46774         },
46775         height:  {
46776             title: "Height",
46777             width: 40
46778         },
46779         align: {
46780             title: "Align",
46781             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46782             width : 80
46783             
46784         },
46785         border: {
46786             title: "Border",
46787             width: 40
46788         },
46789         alt: {
46790             title: "Alt",
46791             width: 120
46792         },
46793         src : {
46794             title: "Src",
46795             width: 220
46796         }
46797         
46798     },
46799     'A' : {
46800         name : {
46801             title: "Name",
46802             width: 50
46803         },
46804         target:  {
46805             title: "Target",
46806             width: 120
46807         },
46808         href:  {
46809             title: "Href",
46810             width: 220
46811         } // border?
46812         
46813     },
46814     'TABLE' : {
46815         rows : {
46816             title: "Rows",
46817             width: 20
46818         },
46819         cols : {
46820             title: "Cols",
46821             width: 20
46822         },
46823         width : {
46824             title: "Width",
46825             width: 40
46826         },
46827         height : {
46828             title: "Height",
46829             width: 40
46830         },
46831         border : {
46832             title: "Border",
46833             width: 20
46834         }
46835     },
46836     'TD' : {
46837         width : {
46838             title: "Width",
46839             width: 40
46840         },
46841         height : {
46842             title: "Height",
46843             width: 40
46844         },   
46845         align: {
46846             title: "Align",
46847             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46848             width: 80
46849         },
46850         valign: {
46851             title: "Valign",
46852             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46853             width: 80
46854         },
46855         colspan: {
46856             title: "Colspan",
46857             width: 20
46858             
46859         },
46860          'font-family'  : {
46861             title : "Font",
46862             style : 'fontFamily',
46863             displayField: 'display',
46864             optname : 'font-family',
46865             width: 140
46866         }
46867     },
46868     'INPUT' : {
46869         name : {
46870             title: "name",
46871             width: 120
46872         },
46873         value : {
46874             title: "Value",
46875             width: 120
46876         },
46877         width : {
46878             title: "Width",
46879             width: 40
46880         }
46881     },
46882     'LABEL' : {
46883         'for' : {
46884             title: "For",
46885             width: 120
46886         }
46887     },
46888     'TEXTAREA' : {
46889           name : {
46890             title: "name",
46891             width: 120
46892         },
46893         rows : {
46894             title: "Rows",
46895             width: 20
46896         },
46897         cols : {
46898             title: "Cols",
46899             width: 20
46900         }
46901     },
46902     'SELECT' : {
46903         name : {
46904             title: "name",
46905             width: 120
46906         },
46907         selectoptions : {
46908             title: "Options",
46909             width: 200
46910         }
46911     },
46912     
46913     // should we really allow this??
46914     // should this just be 
46915     'BODY' : {
46916         title : {
46917             title: "Title",
46918             width: 200,
46919             disabled : true
46920         }
46921     },
46922     'SPAN' : {
46923         'font-family'  : {
46924             title : "Font",
46925             style : 'fontFamily',
46926             displayField: 'display',
46927             optname : 'font-family',
46928             width: 140
46929         }
46930     },
46931     'DIV' : {
46932         'font-family'  : {
46933             title : "Font",
46934             style : 'fontFamily',
46935             displayField: 'display',
46936             optname : 'font-family',
46937             width: 140
46938         }
46939     },
46940      'P' : {
46941         'font-family'  : {
46942             title : "Font",
46943             style : 'fontFamily',
46944             displayField: 'display',
46945             optname : 'font-family',
46946             width: 140
46947         }
46948     },
46949     
46950     '*' : {
46951         // empty..
46952     }
46953
46954 };
46955
46956 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
46957 Roo.form.HtmlEditor.ToolbarContext.stores = false;
46958
46959 Roo.form.HtmlEditor.ToolbarContext.options = {
46960         'font-family'  : [ 
46961                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
46962                 [ 'Courier New', 'Courier New'],
46963                 [ 'Tahoma', 'Tahoma'],
46964                 [ 'Times New Roman,serif', 'Times'],
46965                 [ 'Verdana','Verdana' ]
46966         ]
46967 };
46968
46969 // fixme - these need to be configurable..
46970  
46971
46972 //Roo.form.HtmlEditor.ToolbarContext.types
46973
46974
46975 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
46976     
46977     tb: false,
46978     
46979     rendered: false,
46980     
46981     editor : false,
46982     editorcore : false,
46983     /**
46984      * @cfg {Object} disable  List of toolbar elements to disable
46985          
46986      */
46987     disable : false,
46988     /**
46989      * @cfg {Object} styles List of styles 
46990      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
46991      *
46992      * These must be defined in the page, so they get rendered correctly..
46993      * .headline { }
46994      * TD.underline { }
46995      * 
46996      */
46997     styles : false,
46998     
46999     options: false,
47000     
47001     toolbars : false,
47002     
47003     init : function(editor)
47004     {
47005         this.editor = editor;
47006         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47007         var editorcore = this.editorcore;
47008         
47009         var fid = editorcore.frameId;
47010         var etb = this;
47011         function btn(id, toggle, handler){
47012             var xid = fid + '-'+ id ;
47013             return {
47014                 id : xid,
47015                 cmd : id,
47016                 cls : 'x-btn-icon x-edit-'+id,
47017                 enableToggle:toggle !== false,
47018                 scope: editorcore, // was editor...
47019                 handler:handler||editorcore.relayBtnCmd,
47020                 clickEvent:'mousedown',
47021                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47022                 tabIndex:-1
47023             };
47024         }
47025         // create a new element.
47026         var wdiv = editor.wrap.createChild({
47027                 tag: 'div'
47028             }, editor.wrap.dom.firstChild.nextSibling, true);
47029         
47030         // can we do this more than once??
47031         
47032          // stop form submits
47033       
47034  
47035         // disable everything...
47036         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47037         this.toolbars = {};
47038            
47039         for (var i in  ty) {
47040           
47041             this.toolbars[i] = this.buildToolbar(ty[i],i);
47042         }
47043         this.tb = this.toolbars.BODY;
47044         this.tb.el.show();
47045         this.buildFooter();
47046         this.footer.show();
47047         editor.on('hide', function( ) { this.footer.hide() }, this);
47048         editor.on('show', function( ) { this.footer.show() }, this);
47049         
47050          
47051         this.rendered = true;
47052         
47053         // the all the btns;
47054         editor.on('editorevent', this.updateToolbar, this);
47055         // other toolbars need to implement this..
47056         //editor.on('editmodechange', this.updateToolbar, this);
47057     },
47058     
47059     
47060     
47061     /**
47062      * Protected method that will not generally be called directly. It triggers
47063      * a toolbar update by reading the markup state of the current selection in the editor.
47064      *
47065      * Note you can force an update by calling on('editorevent', scope, false)
47066      */
47067     updateToolbar: function(editor,ev,sel){
47068
47069         //Roo.log(ev);
47070         // capture mouse up - this is handy for selecting images..
47071         // perhaps should go somewhere else...
47072         if(!this.editorcore.activated){
47073              this.editor.onFirstFocus();
47074             return;
47075         }
47076         
47077         
47078         
47079         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47080         // selectNode - might want to handle IE?
47081         if (ev &&
47082             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47083             ev.target && ev.target.tagName == 'IMG') {
47084             // they have click on an image...
47085             // let's see if we can change the selection...
47086             sel = ev.target;
47087          
47088               var nodeRange = sel.ownerDocument.createRange();
47089             try {
47090                 nodeRange.selectNode(sel);
47091             } catch (e) {
47092                 nodeRange.selectNodeContents(sel);
47093             }
47094             //nodeRange.collapse(true);
47095             var s = this.editorcore.win.getSelection();
47096             s.removeAllRanges();
47097             s.addRange(nodeRange);
47098         }  
47099         
47100       
47101         var updateFooter = sel ? false : true;
47102         
47103         
47104         var ans = this.editorcore.getAllAncestors();
47105         
47106         // pick
47107         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47108         
47109         if (!sel) { 
47110             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47111             sel = sel ? sel : this.editorcore.doc.body;
47112             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47113             
47114         }
47115         // pick a menu that exists..
47116         var tn = sel.tagName.toUpperCase();
47117         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47118         
47119         tn = sel.tagName.toUpperCase();
47120         
47121         var lastSel = this.tb.selectedNode;
47122         
47123         this.tb.selectedNode = sel;
47124         
47125         // if current menu does not match..
47126         
47127         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47128                 
47129             this.tb.el.hide();
47130             ///console.log("show: " + tn);
47131             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47132             this.tb.el.show();
47133             // update name
47134             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47135             
47136             
47137             // update attributes
47138             if (this.tb.fields) {
47139                 this.tb.fields.each(function(e) {
47140                     if (e.stylename) {
47141                         e.setValue(sel.style[e.stylename]);
47142                         return;
47143                     } 
47144                    e.setValue(sel.getAttribute(e.attrname));
47145                 });
47146             }
47147             
47148             var hasStyles = false;
47149             for(var i in this.styles) {
47150                 hasStyles = true;
47151                 break;
47152             }
47153             
47154             // update styles
47155             if (hasStyles) { 
47156                 var st = this.tb.fields.item(0);
47157                 
47158                 st.store.removeAll();
47159                
47160                 
47161                 var cn = sel.className.split(/\s+/);
47162                 
47163                 var avs = [];
47164                 if (this.styles['*']) {
47165                     
47166                     Roo.each(this.styles['*'], function(v) {
47167                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47168                     });
47169                 }
47170                 if (this.styles[tn]) { 
47171                     Roo.each(this.styles[tn], function(v) {
47172                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47173                     });
47174                 }
47175                 
47176                 st.store.loadData(avs);
47177                 st.collapse();
47178                 st.setValue(cn);
47179             }
47180             // flag our selected Node.
47181             this.tb.selectedNode = sel;
47182            
47183            
47184             Roo.menu.MenuMgr.hideAll();
47185
47186         }
47187         
47188         if (!updateFooter) {
47189             //this.footDisp.dom.innerHTML = ''; 
47190             return;
47191         }
47192         // update the footer
47193         //
47194         var html = '';
47195         
47196         this.footerEls = ans.reverse();
47197         Roo.each(this.footerEls, function(a,i) {
47198             if (!a) { return; }
47199             html += html.length ? ' &gt; '  :  '';
47200             
47201             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47202             
47203         });
47204        
47205         // 
47206         var sz = this.footDisp.up('td').getSize();
47207         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47208         this.footDisp.dom.style.marginLeft = '5px';
47209         
47210         this.footDisp.dom.style.overflow = 'hidden';
47211         
47212         this.footDisp.dom.innerHTML = html;
47213             
47214         //this.editorsyncValue();
47215     },
47216      
47217     
47218    
47219        
47220     // private
47221     onDestroy : function(){
47222         if(this.rendered){
47223             
47224             this.tb.items.each(function(item){
47225                 if(item.menu){
47226                     item.menu.removeAll();
47227                     if(item.menu.el){
47228                         item.menu.el.destroy();
47229                     }
47230                 }
47231                 item.destroy();
47232             });
47233              
47234         }
47235     },
47236     onFirstFocus: function() {
47237         // need to do this for all the toolbars..
47238         this.tb.items.each(function(item){
47239            item.enable();
47240         });
47241     },
47242     buildToolbar: function(tlist, nm)
47243     {
47244         var editor = this.editor;
47245         var editorcore = this.editorcore;
47246          // create a new element.
47247         var wdiv = editor.wrap.createChild({
47248                 tag: 'div'
47249             }, editor.wrap.dom.firstChild.nextSibling, true);
47250         
47251        
47252         var tb = new Roo.Toolbar(wdiv);
47253         // add the name..
47254         
47255         tb.add(nm+ ":&nbsp;");
47256         
47257         var styles = [];
47258         for(var i in this.styles) {
47259             styles.push(i);
47260         }
47261         
47262         // styles...
47263         if (styles && styles.length) {
47264             
47265             // this needs a multi-select checkbox...
47266             tb.addField( new Roo.form.ComboBox({
47267                 store: new Roo.data.SimpleStore({
47268                     id : 'val',
47269                     fields: ['val', 'selected'],
47270                     data : [] 
47271                 }),
47272                 name : '-roo-edit-className',
47273                 attrname : 'className',
47274                 displayField: 'val',
47275                 typeAhead: false,
47276                 mode: 'local',
47277                 editable : false,
47278                 triggerAction: 'all',
47279                 emptyText:'Select Style',
47280                 selectOnFocus:true,
47281                 width: 130,
47282                 listeners : {
47283                     'select': function(c, r, i) {
47284                         // initial support only for on class per el..
47285                         tb.selectedNode.className =  r ? r.get('val') : '';
47286                         editorcore.syncValue();
47287                     }
47288                 }
47289     
47290             }));
47291         }
47292         
47293         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47294         var tbops = tbc.options;
47295         
47296         for (var i in tlist) {
47297             
47298             var item = tlist[i];
47299             tb.add(item.title + ":&nbsp;");
47300             
47301             
47302             //optname == used so you can configure the options available..
47303             var opts = item.opts ? item.opts : false;
47304             if (item.optname) {
47305                 opts = tbops[item.optname];
47306            
47307             }
47308             
47309             if (opts) {
47310                 // opts == pulldown..
47311                 tb.addField( new Roo.form.ComboBox({
47312                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47313                         id : 'val',
47314                         fields: ['val', 'display'],
47315                         data : opts  
47316                     }),
47317                     name : '-roo-edit-' + i,
47318                     attrname : i,
47319                     stylename : item.style ? item.style : false,
47320                     displayField: item.displayField ? item.displayField : 'val',
47321                     valueField :  'val',
47322                     typeAhead: false,
47323                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47324                     editable : false,
47325                     triggerAction: 'all',
47326                     emptyText:'Select',
47327                     selectOnFocus:true,
47328                     width: item.width ? item.width  : 130,
47329                     listeners : {
47330                         'select': function(c, r, i) {
47331                             if (c.stylename) {
47332                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47333                                 return;
47334                             }
47335                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47336                         }
47337                     }
47338
47339                 }));
47340                 continue;
47341                     
47342                  
47343                 
47344                 tb.addField( new Roo.form.TextField({
47345                     name: i,
47346                     width: 100,
47347                     //allowBlank:false,
47348                     value: ''
47349                 }));
47350                 continue;
47351             }
47352             tb.addField( new Roo.form.TextField({
47353                 name: '-roo-edit-' + i,
47354                 attrname : i,
47355                 
47356                 width: item.width,
47357                 //allowBlank:true,
47358                 value: '',
47359                 listeners: {
47360                     'change' : function(f, nv, ov) {
47361                         tb.selectedNode.setAttribute(f.attrname, nv);
47362                         editorcore.syncValue();
47363                     }
47364                 }
47365             }));
47366              
47367         }
47368         
47369         var _this = this;
47370         
47371         if(nm == 'BODY'){
47372             tb.addSeparator();
47373         
47374             tb.addButton( {
47375                 text: 'Stylesheets',
47376
47377                 listeners : {
47378                     click : function ()
47379                     {
47380                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47381                     }
47382                 }
47383             });
47384         }
47385         
47386         tb.addFill();
47387         tb.addButton( {
47388             text: 'Remove Tag',
47389     
47390             listeners : {
47391                 click : function ()
47392                 {
47393                     // remove
47394                     // undo does not work.
47395                      
47396                     var sn = tb.selectedNode;
47397                     
47398                     var pn = sn.parentNode;
47399                     
47400                     var stn =  sn.childNodes[0];
47401                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47402                     while (sn.childNodes.length) {
47403                         var node = sn.childNodes[0];
47404                         sn.removeChild(node);
47405                         //Roo.log(node);
47406                         pn.insertBefore(node, sn);
47407                         
47408                     }
47409                     pn.removeChild(sn);
47410                     var range = editorcore.createRange();
47411         
47412                     range.setStart(stn,0);
47413                     range.setEnd(en,0); //????
47414                     //range.selectNode(sel);
47415                     
47416                     
47417                     var selection = editorcore.getSelection();
47418                     selection.removeAllRanges();
47419                     selection.addRange(range);
47420                     
47421                     
47422                     
47423                     //_this.updateToolbar(null, null, pn);
47424                     _this.updateToolbar(null, null, null);
47425                     _this.footDisp.dom.innerHTML = ''; 
47426                 }
47427             }
47428             
47429                     
47430                 
47431             
47432         });
47433         
47434         
47435         tb.el.on('click', function(e){
47436             e.preventDefault(); // what does this do?
47437         });
47438         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47439         tb.el.hide();
47440         tb.name = nm;
47441         // dont need to disable them... as they will get hidden
47442         return tb;
47443          
47444         
47445     },
47446     buildFooter : function()
47447     {
47448         
47449         var fel = this.editor.wrap.createChild();
47450         this.footer = new Roo.Toolbar(fel);
47451         // toolbar has scrolly on left / right?
47452         var footDisp= new Roo.Toolbar.Fill();
47453         var _t = this;
47454         this.footer.add(
47455             {
47456                 text : '&lt;',
47457                 xtype: 'Button',
47458                 handler : function() {
47459                     _t.footDisp.scrollTo('left',0,true)
47460                 }
47461             }
47462         );
47463         this.footer.add( footDisp );
47464         this.footer.add( 
47465             {
47466                 text : '&gt;',
47467                 xtype: 'Button',
47468                 handler : function() {
47469                     // no animation..
47470                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47471                 }
47472             }
47473         );
47474         var fel = Roo.get(footDisp.el);
47475         fel.addClass('x-editor-context');
47476         this.footDispWrap = fel; 
47477         this.footDispWrap.overflow  = 'hidden';
47478         
47479         this.footDisp = fel.createChild();
47480         this.footDispWrap.on('click', this.onContextClick, this)
47481         
47482         
47483     },
47484     onContextClick : function (ev,dom)
47485     {
47486         ev.preventDefault();
47487         var  cn = dom.className;
47488         //Roo.log(cn);
47489         if (!cn.match(/x-ed-loc-/)) {
47490             return;
47491         }
47492         var n = cn.split('-').pop();
47493         var ans = this.footerEls;
47494         var sel = ans[n];
47495         
47496          // pick
47497         var range = this.editorcore.createRange();
47498         
47499         range.selectNodeContents(sel);
47500         //range.selectNode(sel);
47501         
47502         
47503         var selection = this.editorcore.getSelection();
47504         selection.removeAllRanges();
47505         selection.addRange(range);
47506         
47507         
47508         
47509         this.updateToolbar(null, null, sel);
47510         
47511         
47512     }
47513     
47514     
47515     
47516     
47517     
47518 });
47519
47520
47521
47522
47523
47524 /*
47525  * Based on:
47526  * Ext JS Library 1.1.1
47527  * Copyright(c) 2006-2007, Ext JS, LLC.
47528  *
47529  * Originally Released Under LGPL - original licence link has changed is not relivant.
47530  *
47531  * Fork - LGPL
47532  * <script type="text/javascript">
47533  */
47534  
47535 /**
47536  * @class Roo.form.BasicForm
47537  * @extends Roo.util.Observable
47538  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47539  * @constructor
47540  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47541  * @param {Object} config Configuration options
47542  */
47543 Roo.form.BasicForm = function(el, config){
47544     this.allItems = [];
47545     this.childForms = [];
47546     Roo.apply(this, config);
47547     /*
47548      * The Roo.form.Field items in this form.
47549      * @type MixedCollection
47550      */
47551      
47552      
47553     this.items = new Roo.util.MixedCollection(false, function(o){
47554         return o.id || (o.id = Roo.id());
47555     });
47556     this.addEvents({
47557         /**
47558          * @event beforeaction
47559          * Fires before any action is performed. Return false to cancel the action.
47560          * @param {Form} this
47561          * @param {Action} action The action to be performed
47562          */
47563         beforeaction: true,
47564         /**
47565          * @event actionfailed
47566          * Fires when an action fails.
47567          * @param {Form} this
47568          * @param {Action} action The action that failed
47569          */
47570         actionfailed : true,
47571         /**
47572          * @event actioncomplete
47573          * Fires when an action is completed.
47574          * @param {Form} this
47575          * @param {Action} action The action that completed
47576          */
47577         actioncomplete : true
47578     });
47579     if(el){
47580         this.initEl(el);
47581     }
47582     Roo.form.BasicForm.superclass.constructor.call(this);
47583     
47584     Roo.form.BasicForm.popover.apply();
47585 };
47586
47587 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47588     /**
47589      * @cfg {String} method
47590      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47591      */
47592     /**
47593      * @cfg {DataReader} reader
47594      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47595      * This is optional as there is built-in support for processing JSON.
47596      */
47597     /**
47598      * @cfg {DataReader} errorReader
47599      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47600      * This is completely optional as there is built-in support for processing JSON.
47601      */
47602     /**
47603      * @cfg {String} url
47604      * The URL to use for form actions if one isn't supplied in the action options.
47605      */
47606     /**
47607      * @cfg {Boolean} fileUpload
47608      * Set to true if this form is a file upload.
47609      */
47610      
47611     /**
47612      * @cfg {Object} baseParams
47613      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47614      */
47615      /**
47616      
47617     /**
47618      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47619      */
47620     timeout: 30,
47621
47622     // private
47623     activeAction : null,
47624
47625     /**
47626      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47627      * or setValues() data instead of when the form was first created.
47628      */
47629     trackResetOnLoad : false,
47630     
47631     
47632     /**
47633      * childForms - used for multi-tab forms
47634      * @type {Array}
47635      */
47636     childForms : false,
47637     
47638     /**
47639      * allItems - full list of fields.
47640      * @type {Array}
47641      */
47642     allItems : false,
47643     
47644     /**
47645      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47646      * element by passing it or its id or mask the form itself by passing in true.
47647      * @type Mixed
47648      */
47649     waitMsgTarget : false,
47650     
47651     /**
47652      * @type Boolean
47653      */
47654     disableMask : false,
47655     
47656     /**
47657      * @cfg {Boolean} errorMask (true|false) default false
47658      */
47659     errorMask : false,
47660     
47661     /**
47662      * @cfg {Number} maskOffset Default 100
47663      */
47664     maskOffset : 100,
47665
47666     // private
47667     initEl : function(el){
47668         this.el = Roo.get(el);
47669         this.id = this.el.id || Roo.id();
47670         this.el.on('submit', this.onSubmit, this);
47671         this.el.addClass('x-form');
47672     },
47673
47674     // private
47675     onSubmit : function(e){
47676         e.stopEvent();
47677     },
47678
47679     /**
47680      * Returns true if client-side validation on the form is successful.
47681      * @return Boolean
47682      */
47683     isValid : function(){
47684         var valid = true;
47685         var target = false;
47686         this.items.each(function(f){
47687             if(f.validate()){
47688                 return;
47689             }
47690             
47691             valid = false;
47692                 
47693             if(!target && f.el.isVisible(true)){
47694                 target = f;
47695             }
47696         });
47697         
47698         if(this.errorMask && !valid){
47699             Roo.form.BasicForm.popover.mask(this, target);
47700         }
47701         
47702         return valid;
47703     },
47704     /**
47705      * Returns array of invalid form fields.
47706      * @return Array
47707      */
47708     
47709     invalidFields : function()
47710     {
47711         var ret = [];
47712         this.items.each(function(f){
47713             if(f.validate()){
47714                 return;
47715             }
47716             ret.push(f);
47717             
47718         });
47719         
47720         return ret;
47721     },
47722     
47723     
47724     /**
47725      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47726      * @return Boolean
47727      */
47728     isDirty : function(){
47729         var dirty = false;
47730         this.items.each(function(f){
47731            if(f.isDirty()){
47732                dirty = true;
47733                return false;
47734            }
47735         });
47736         return dirty;
47737     },
47738     
47739     /**
47740      * Returns true if any fields in this form have changed since their original load. (New version)
47741      * @return Boolean
47742      */
47743     
47744     hasChanged : function()
47745     {
47746         var dirty = false;
47747         this.items.each(function(f){
47748            if(f.hasChanged()){
47749                dirty = true;
47750                return false;
47751            }
47752         });
47753         return dirty;
47754         
47755     },
47756     /**
47757      * Resets all hasChanged to 'false' -
47758      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47759      * So hasChanged storage is only to be used for this purpose
47760      * @return Boolean
47761      */
47762     resetHasChanged : function()
47763     {
47764         this.items.each(function(f){
47765            f.resetHasChanged();
47766         });
47767         
47768     },
47769     
47770     
47771     /**
47772      * Performs a predefined action (submit or load) or custom actions you define on this form.
47773      * @param {String} actionName The name of the action type
47774      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47775      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47776      * accept other config options):
47777      * <pre>
47778 Property          Type             Description
47779 ----------------  ---------------  ----------------------------------------------------------------------------------
47780 url               String           The url for the action (defaults to the form's url)
47781 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47782 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47783 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47784                                    validate the form on the client (defaults to false)
47785      * </pre>
47786      * @return {BasicForm} this
47787      */
47788     doAction : function(action, options){
47789         if(typeof action == 'string'){
47790             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47791         }
47792         if(this.fireEvent('beforeaction', this, action) !== false){
47793             this.beforeAction(action);
47794             action.run.defer(100, action);
47795         }
47796         return this;
47797     },
47798
47799     /**
47800      * Shortcut to do a submit action.
47801      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47802      * @return {BasicForm} this
47803      */
47804     submit : function(options){
47805         this.doAction('submit', options);
47806         return this;
47807     },
47808
47809     /**
47810      * Shortcut to do a load action.
47811      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47812      * @return {BasicForm} this
47813      */
47814     load : function(options){
47815         this.doAction('load', options);
47816         return this;
47817     },
47818
47819     /**
47820      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47821      * @param {Record} record The record to edit
47822      * @return {BasicForm} this
47823      */
47824     updateRecord : function(record){
47825         record.beginEdit();
47826         var fs = record.fields;
47827         fs.each(function(f){
47828             var field = this.findField(f.name);
47829             if(field){
47830                 record.set(f.name, field.getValue());
47831             }
47832         }, this);
47833         record.endEdit();
47834         return this;
47835     },
47836
47837     /**
47838      * Loads an Roo.data.Record into this form.
47839      * @param {Record} record The record to load
47840      * @return {BasicForm} this
47841      */
47842     loadRecord : function(record){
47843         this.setValues(record.data);
47844         return this;
47845     },
47846
47847     // private
47848     beforeAction : function(action){
47849         var o = action.options;
47850         
47851         if(!this.disableMask) {
47852             if(this.waitMsgTarget === true){
47853                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47854             }else if(this.waitMsgTarget){
47855                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47856                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47857             }else {
47858                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47859             }
47860         }
47861         
47862          
47863     },
47864
47865     // private
47866     afterAction : function(action, success){
47867         this.activeAction = null;
47868         var o = action.options;
47869         
47870         if(!this.disableMask) {
47871             if(this.waitMsgTarget === true){
47872                 this.el.unmask();
47873             }else if(this.waitMsgTarget){
47874                 this.waitMsgTarget.unmask();
47875             }else{
47876                 Roo.MessageBox.updateProgress(1);
47877                 Roo.MessageBox.hide();
47878             }
47879         }
47880         
47881         if(success){
47882             if(o.reset){
47883                 this.reset();
47884             }
47885             Roo.callback(o.success, o.scope, [this, action]);
47886             this.fireEvent('actioncomplete', this, action);
47887             
47888         }else{
47889             
47890             // failure condition..
47891             // we have a scenario where updates need confirming.
47892             // eg. if a locking scenario exists..
47893             // we look for { errors : { needs_confirm : true }} in the response.
47894             if (
47895                 (typeof(action.result) != 'undefined')  &&
47896                 (typeof(action.result.errors) != 'undefined')  &&
47897                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47898            ){
47899                 var _t = this;
47900                 Roo.MessageBox.confirm(
47901                     "Change requires confirmation",
47902                     action.result.errorMsg,
47903                     function(r) {
47904                         if (r != 'yes') {
47905                             return;
47906                         }
47907                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47908                     }
47909                     
47910                 );
47911                 
47912                 
47913                 
47914                 return;
47915             }
47916             
47917             Roo.callback(o.failure, o.scope, [this, action]);
47918             // show an error message if no failed handler is set..
47919             if (!this.hasListener('actionfailed')) {
47920                 Roo.MessageBox.alert("Error",
47921                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47922                         action.result.errorMsg :
47923                         "Saving Failed, please check your entries or try again"
47924                 );
47925             }
47926             
47927             this.fireEvent('actionfailed', this, action);
47928         }
47929         
47930     },
47931
47932     /**
47933      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47934      * @param {String} id The value to search for
47935      * @return Field
47936      */
47937     findField : function(id){
47938         var field = this.items.get(id);
47939         if(!field){
47940             this.items.each(function(f){
47941                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
47942                     field = f;
47943                     return false;
47944                 }
47945             });
47946         }
47947         return field || null;
47948     },
47949
47950     /**
47951      * Add a secondary form to this one, 
47952      * Used to provide tabbed forms. One form is primary, with hidden values 
47953      * which mirror the elements from the other forms.
47954      * 
47955      * @param {Roo.form.Form} form to add.
47956      * 
47957      */
47958     addForm : function(form)
47959     {
47960        
47961         if (this.childForms.indexOf(form) > -1) {
47962             // already added..
47963             return;
47964         }
47965         this.childForms.push(form);
47966         var n = '';
47967         Roo.each(form.allItems, function (fe) {
47968             
47969             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
47970             if (this.findField(n)) { // already added..
47971                 return;
47972             }
47973             var add = new Roo.form.Hidden({
47974                 name : n
47975             });
47976             add.render(this.el);
47977             
47978             this.add( add );
47979         }, this);
47980         
47981     },
47982     /**
47983      * Mark fields in this form invalid in bulk.
47984      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
47985      * @return {BasicForm} this
47986      */
47987     markInvalid : function(errors){
47988         if(errors instanceof Array){
47989             for(var i = 0, len = errors.length; i < len; i++){
47990                 var fieldError = errors[i];
47991                 var f = this.findField(fieldError.id);
47992                 if(f){
47993                     f.markInvalid(fieldError.msg);
47994                 }
47995             }
47996         }else{
47997             var field, id;
47998             for(id in errors){
47999                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48000                     field.markInvalid(errors[id]);
48001                 }
48002             }
48003         }
48004         Roo.each(this.childForms || [], function (f) {
48005             f.markInvalid(errors);
48006         });
48007         
48008         return this;
48009     },
48010
48011     /**
48012      * Set values for fields in this form in bulk.
48013      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48014      * @return {BasicForm} this
48015      */
48016     setValues : function(values){
48017         if(values instanceof Array){ // array of objects
48018             for(var i = 0, len = values.length; i < len; i++){
48019                 var v = values[i];
48020                 var f = this.findField(v.id);
48021                 if(f){
48022                     f.setValue(v.value);
48023                     if(this.trackResetOnLoad){
48024                         f.originalValue = f.getValue();
48025                     }
48026                 }
48027             }
48028         }else{ // object hash
48029             var field, id;
48030             for(id in values){
48031                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48032                     
48033                     if (field.setFromData && 
48034                         field.valueField && 
48035                         field.displayField &&
48036                         // combos' with local stores can 
48037                         // be queried via setValue()
48038                         // to set their value..
48039                         (field.store && !field.store.isLocal)
48040                         ) {
48041                         // it's a combo
48042                         var sd = { };
48043                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48044                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48045                         field.setFromData(sd);
48046                         
48047                     } else {
48048                         field.setValue(values[id]);
48049                     }
48050                     
48051                     
48052                     if(this.trackResetOnLoad){
48053                         field.originalValue = field.getValue();
48054                     }
48055                 }
48056             }
48057         }
48058         this.resetHasChanged();
48059         
48060         
48061         Roo.each(this.childForms || [], function (f) {
48062             f.setValues(values);
48063             f.resetHasChanged();
48064         });
48065                 
48066         return this;
48067     },
48068  
48069     /**
48070      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48071      * they are returned as an array.
48072      * @param {Boolean} asString
48073      * @return {Object}
48074      */
48075     getValues : function(asString){
48076         if (this.childForms) {
48077             // copy values from the child forms
48078             Roo.each(this.childForms, function (f) {
48079                 this.setValues(f.getValues());
48080             }, this);
48081         }
48082         
48083         // use formdata
48084         if (typeof(FormData) != 'undefined' && asString !== true) {
48085             // this relies on a 'recent' version of chrome apparently...
48086             try {
48087                 var fd = (new FormData(this.el.dom)).entries();
48088                 var ret = {};
48089                 var ent = fd.next();
48090                 while (!ent.done) {
48091                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48092                     ent = fd.next();
48093                 };
48094                 return ret;
48095             } catch(e) {
48096                 
48097             }
48098             
48099         }
48100         
48101         
48102         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48103         if(asString === true){
48104             return fs;
48105         }
48106         return Roo.urlDecode(fs);
48107     },
48108     
48109     /**
48110      * Returns the fields in this form as an object with key/value pairs. 
48111      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48112      * @return {Object}
48113      */
48114     getFieldValues : function(with_hidden)
48115     {
48116         if (this.childForms) {
48117             // copy values from the child forms
48118             // should this call getFieldValues - probably not as we do not currently copy
48119             // hidden fields when we generate..
48120             Roo.each(this.childForms, function (f) {
48121                 this.setValues(f.getValues());
48122             }, this);
48123         }
48124         
48125         var ret = {};
48126         this.items.each(function(f){
48127             if (!f.getName()) {
48128                 return;
48129             }
48130             var v = f.getValue();
48131             if (f.inputType =='radio') {
48132                 if (typeof(ret[f.getName()]) == 'undefined') {
48133                     ret[f.getName()] = ''; // empty..
48134                 }
48135                 
48136                 if (!f.el.dom.checked) {
48137                     return;
48138                     
48139                 }
48140                 v = f.el.dom.value;
48141                 
48142             }
48143             
48144             // not sure if this supported any more..
48145             if ((typeof(v) == 'object') && f.getRawValue) {
48146                 v = f.getRawValue() ; // dates..
48147             }
48148             // combo boxes where name != hiddenName...
48149             if (f.name != f.getName()) {
48150                 ret[f.name] = f.getRawValue();
48151             }
48152             ret[f.getName()] = v;
48153         });
48154         
48155         return ret;
48156     },
48157
48158     /**
48159      * Clears all invalid messages in this form.
48160      * @return {BasicForm} this
48161      */
48162     clearInvalid : function(){
48163         this.items.each(function(f){
48164            f.clearInvalid();
48165         });
48166         
48167         Roo.each(this.childForms || [], function (f) {
48168             f.clearInvalid();
48169         });
48170         
48171         
48172         return this;
48173     },
48174
48175     /**
48176      * Resets this form.
48177      * @return {BasicForm} this
48178      */
48179     reset : function(){
48180         this.items.each(function(f){
48181             f.reset();
48182         });
48183         
48184         Roo.each(this.childForms || [], function (f) {
48185             f.reset();
48186         });
48187         this.resetHasChanged();
48188         
48189         return this;
48190     },
48191
48192     /**
48193      * Add Roo.form components to this form.
48194      * @param {Field} field1
48195      * @param {Field} field2 (optional)
48196      * @param {Field} etc (optional)
48197      * @return {BasicForm} this
48198      */
48199     add : function(){
48200         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48201         return this;
48202     },
48203
48204
48205     /**
48206      * Removes a field from the items collection (does NOT remove its markup).
48207      * @param {Field} field
48208      * @return {BasicForm} this
48209      */
48210     remove : function(field){
48211         this.items.remove(field);
48212         return this;
48213     },
48214
48215     /**
48216      * Looks at the fields in this form, checks them for an id attribute,
48217      * and calls applyTo on the existing dom element with that id.
48218      * @return {BasicForm} this
48219      */
48220     render : function(){
48221         this.items.each(function(f){
48222             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48223                 f.applyTo(f.id);
48224             }
48225         });
48226         return this;
48227     },
48228
48229     /**
48230      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48231      * @param {Object} values
48232      * @return {BasicForm} this
48233      */
48234     applyToFields : function(o){
48235         this.items.each(function(f){
48236            Roo.apply(f, o);
48237         });
48238         return this;
48239     },
48240
48241     /**
48242      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48243      * @param {Object} values
48244      * @return {BasicForm} this
48245      */
48246     applyIfToFields : function(o){
48247         this.items.each(function(f){
48248            Roo.applyIf(f, o);
48249         });
48250         return this;
48251     }
48252 });
48253
48254 // back compat
48255 Roo.BasicForm = Roo.form.BasicForm;
48256
48257 Roo.apply(Roo.form.BasicForm, {
48258     
48259     popover : {
48260         
48261         padding : 5,
48262         
48263         isApplied : false,
48264         
48265         isMasked : false,
48266         
48267         form : false,
48268         
48269         target : false,
48270         
48271         intervalID : false,
48272         
48273         maskEl : false,
48274         
48275         apply : function()
48276         {
48277             if(this.isApplied){
48278                 return;
48279             }
48280             
48281             this.maskEl = {
48282                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48283                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48284                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48285                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48286             };
48287             
48288             this.maskEl.top.enableDisplayMode("block");
48289             this.maskEl.left.enableDisplayMode("block");
48290             this.maskEl.bottom.enableDisplayMode("block");
48291             this.maskEl.right.enableDisplayMode("block");
48292             
48293             Roo.get(document.body).on('click', function(){
48294                 this.unmask();
48295             }, this);
48296             
48297             Roo.get(document.body).on('touchstart', function(){
48298                 this.unmask();
48299             }, this);
48300             
48301             this.isApplied = true
48302         },
48303         
48304         mask : function(form, target)
48305         {
48306             this.form = form;
48307             
48308             this.target = target;
48309             
48310             if(!this.form.errorMask || !target.el){
48311                 return;
48312             }
48313             
48314             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48315             
48316             var ot = this.target.el.calcOffsetsTo(scrollable);
48317             
48318             var scrollTo = ot[1] - this.form.maskOffset;
48319             
48320             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48321             
48322             scrollable.scrollTo('top', scrollTo);
48323             
48324             var el = this.target.wrap || this.target.el;
48325             
48326             var box = el.getBox();
48327             
48328             this.maskEl.top.setStyle('position', 'absolute');
48329             this.maskEl.top.setStyle('z-index', 10000);
48330             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48331             this.maskEl.top.setLeft(0);
48332             this.maskEl.top.setTop(0);
48333             this.maskEl.top.show();
48334             
48335             this.maskEl.left.setStyle('position', 'absolute');
48336             this.maskEl.left.setStyle('z-index', 10000);
48337             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48338             this.maskEl.left.setLeft(0);
48339             this.maskEl.left.setTop(box.y - this.padding);
48340             this.maskEl.left.show();
48341
48342             this.maskEl.bottom.setStyle('position', 'absolute');
48343             this.maskEl.bottom.setStyle('z-index', 10000);
48344             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48345             this.maskEl.bottom.setLeft(0);
48346             this.maskEl.bottom.setTop(box.bottom + this.padding);
48347             this.maskEl.bottom.show();
48348
48349             this.maskEl.right.setStyle('position', 'absolute');
48350             this.maskEl.right.setStyle('z-index', 10000);
48351             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48352             this.maskEl.right.setLeft(box.right + this.padding);
48353             this.maskEl.right.setTop(box.y - this.padding);
48354             this.maskEl.right.show();
48355
48356             this.intervalID = window.setInterval(function() {
48357                 Roo.form.BasicForm.popover.unmask();
48358             }, 10000);
48359
48360             window.onwheel = function(){ return false;};
48361             
48362             (function(){ this.isMasked = true; }).defer(500, this);
48363             
48364         },
48365         
48366         unmask : function()
48367         {
48368             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48369                 return;
48370             }
48371             
48372             this.maskEl.top.setStyle('position', 'absolute');
48373             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48374             this.maskEl.top.hide();
48375
48376             this.maskEl.left.setStyle('position', 'absolute');
48377             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48378             this.maskEl.left.hide();
48379
48380             this.maskEl.bottom.setStyle('position', 'absolute');
48381             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48382             this.maskEl.bottom.hide();
48383
48384             this.maskEl.right.setStyle('position', 'absolute');
48385             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48386             this.maskEl.right.hide();
48387             
48388             window.onwheel = function(){ return true;};
48389             
48390             if(this.intervalID){
48391                 window.clearInterval(this.intervalID);
48392                 this.intervalID = false;
48393             }
48394             
48395             this.isMasked = false;
48396             
48397         }
48398         
48399     }
48400     
48401 });/*
48402  * Based on:
48403  * Ext JS Library 1.1.1
48404  * Copyright(c) 2006-2007, Ext JS, LLC.
48405  *
48406  * Originally Released Under LGPL - original licence link has changed is not relivant.
48407  *
48408  * Fork - LGPL
48409  * <script type="text/javascript">
48410  */
48411
48412 /**
48413  * @class Roo.form.Form
48414  * @extends Roo.form.BasicForm
48415  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48416  * @constructor
48417  * @param {Object} config Configuration options
48418  */
48419 Roo.form.Form = function(config){
48420     var xitems =  [];
48421     if (config.items) {
48422         xitems = config.items;
48423         delete config.items;
48424     }
48425    
48426     
48427     Roo.form.Form.superclass.constructor.call(this, null, config);
48428     this.url = this.url || this.action;
48429     if(!this.root){
48430         this.root = new Roo.form.Layout(Roo.applyIf({
48431             id: Roo.id()
48432         }, config));
48433     }
48434     this.active = this.root;
48435     /**
48436      * Array of all the buttons that have been added to this form via {@link addButton}
48437      * @type Array
48438      */
48439     this.buttons = [];
48440     this.allItems = [];
48441     this.addEvents({
48442         /**
48443          * @event clientvalidation
48444          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48445          * @param {Form} this
48446          * @param {Boolean} valid true if the form has passed client-side validation
48447          */
48448         clientvalidation: true,
48449         /**
48450          * @event rendered
48451          * Fires when the form is rendered
48452          * @param {Roo.form.Form} form
48453          */
48454         rendered : true
48455     });
48456     
48457     if (this.progressUrl) {
48458             // push a hidden field onto the list of fields..
48459             this.addxtype( {
48460                     xns: Roo.form, 
48461                     xtype : 'Hidden', 
48462                     name : 'UPLOAD_IDENTIFIER' 
48463             });
48464         }
48465         
48466     
48467     Roo.each(xitems, this.addxtype, this);
48468     
48469 };
48470
48471 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48472     /**
48473      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48474      */
48475     /**
48476      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48477      */
48478     /**
48479      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48480      */
48481     buttonAlign:'center',
48482
48483     /**
48484      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48485      */
48486     minButtonWidth:75,
48487
48488     /**
48489      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48490      * This property cascades to child containers if not set.
48491      */
48492     labelAlign:'left',
48493
48494     /**
48495      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48496      * fires a looping event with that state. This is required to bind buttons to the valid
48497      * state using the config value formBind:true on the button.
48498      */
48499     monitorValid : false,
48500
48501     /**
48502      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48503      */
48504     monitorPoll : 200,
48505     
48506     /**
48507      * @cfg {String} progressUrl - Url to return progress data 
48508      */
48509     
48510     progressUrl : false,
48511     /**
48512      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48513      * sending a formdata with extra parameters - eg uploaded elements.
48514      */
48515     
48516     formData : false,
48517     
48518     /**
48519      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48520      * fields are added and the column is closed. If no fields are passed the column remains open
48521      * until end() is called.
48522      * @param {Object} config The config to pass to the column
48523      * @param {Field} field1 (optional)
48524      * @param {Field} field2 (optional)
48525      * @param {Field} etc (optional)
48526      * @return Column The column container object
48527      */
48528     column : function(c){
48529         var col = new Roo.form.Column(c);
48530         this.start(col);
48531         if(arguments.length > 1){ // duplicate code required because of Opera
48532             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48533             this.end();
48534         }
48535         return col;
48536     },
48537
48538     /**
48539      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48540      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48541      * until end() is called.
48542      * @param {Object} config The config to pass to the fieldset
48543      * @param {Field} field1 (optional)
48544      * @param {Field} field2 (optional)
48545      * @param {Field} etc (optional)
48546      * @return FieldSet The fieldset container object
48547      */
48548     fieldset : function(c){
48549         var fs = new Roo.form.FieldSet(c);
48550         this.start(fs);
48551         if(arguments.length > 1){ // duplicate code required because of Opera
48552             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48553             this.end();
48554         }
48555         return fs;
48556     },
48557
48558     /**
48559      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48560      * fields are added and the container is closed. If no fields are passed the container remains open
48561      * until end() is called.
48562      * @param {Object} config The config to pass to the Layout
48563      * @param {Field} field1 (optional)
48564      * @param {Field} field2 (optional)
48565      * @param {Field} etc (optional)
48566      * @return Layout The container object
48567      */
48568     container : function(c){
48569         var l = new Roo.form.Layout(c);
48570         this.start(l);
48571         if(arguments.length > 1){ // duplicate code required because of Opera
48572             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48573             this.end();
48574         }
48575         return l;
48576     },
48577
48578     /**
48579      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48580      * @param {Object} container A Roo.form.Layout or subclass of Layout
48581      * @return {Form} this
48582      */
48583     start : function(c){
48584         // cascade label info
48585         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48586         this.active.stack.push(c);
48587         c.ownerCt = this.active;
48588         this.active = c;
48589         return this;
48590     },
48591
48592     /**
48593      * Closes the current open container
48594      * @return {Form} this
48595      */
48596     end : function(){
48597         if(this.active == this.root){
48598             return this;
48599         }
48600         this.active = this.active.ownerCt;
48601         return this;
48602     },
48603
48604     /**
48605      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48606      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48607      * as the label of the field.
48608      * @param {Field} field1
48609      * @param {Field} field2 (optional)
48610      * @param {Field} etc. (optional)
48611      * @return {Form} this
48612      */
48613     add : function(){
48614         this.active.stack.push.apply(this.active.stack, arguments);
48615         this.allItems.push.apply(this.allItems,arguments);
48616         var r = [];
48617         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48618             if(a[i].isFormField){
48619                 r.push(a[i]);
48620             }
48621         }
48622         if(r.length > 0){
48623             Roo.form.Form.superclass.add.apply(this, r);
48624         }
48625         return this;
48626     },
48627     
48628
48629     
48630     
48631     
48632      /**
48633      * Find any element that has been added to a form, using it's ID or name
48634      * This can include framesets, columns etc. along with regular fields..
48635      * @param {String} id - id or name to find.
48636      
48637      * @return {Element} e - or false if nothing found.
48638      */
48639     findbyId : function(id)
48640     {
48641         var ret = false;
48642         if (!id) {
48643             return ret;
48644         }
48645         Roo.each(this.allItems, function(f){
48646             if (f.id == id || f.name == id ){
48647                 ret = f;
48648                 return false;
48649             }
48650         });
48651         return ret;
48652     },
48653
48654     
48655     
48656     /**
48657      * Render this form into the passed container. This should only be called once!
48658      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48659      * @return {Form} this
48660      */
48661     render : function(ct)
48662     {
48663         
48664         
48665         
48666         ct = Roo.get(ct);
48667         var o = this.autoCreate || {
48668             tag: 'form',
48669             method : this.method || 'POST',
48670             id : this.id || Roo.id()
48671         };
48672         this.initEl(ct.createChild(o));
48673
48674         this.root.render(this.el);
48675         
48676        
48677              
48678         this.items.each(function(f){
48679             f.render('x-form-el-'+f.id);
48680         });
48681
48682         if(this.buttons.length > 0){
48683             // tables are required to maintain order and for correct IE layout
48684             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48685                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48686                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48687             }}, null, true);
48688             var tr = tb.getElementsByTagName('tr')[0];
48689             for(var i = 0, len = this.buttons.length; i < len; i++) {
48690                 var b = this.buttons[i];
48691                 var td = document.createElement('td');
48692                 td.className = 'x-form-btn-td';
48693                 b.render(tr.appendChild(td));
48694             }
48695         }
48696         if(this.monitorValid){ // initialize after render
48697             this.startMonitoring();
48698         }
48699         this.fireEvent('rendered', this);
48700         return this;
48701     },
48702
48703     /**
48704      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48705      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48706      * object or a valid Roo.DomHelper element config
48707      * @param {Function} handler The function called when the button is clicked
48708      * @param {Object} scope (optional) The scope of the handler function
48709      * @return {Roo.Button}
48710      */
48711     addButton : function(config, handler, scope){
48712         var bc = {
48713             handler: handler,
48714             scope: scope,
48715             minWidth: this.minButtonWidth,
48716             hideParent:true
48717         };
48718         if(typeof config == "string"){
48719             bc.text = config;
48720         }else{
48721             Roo.apply(bc, config);
48722         }
48723         var btn = new Roo.Button(null, bc);
48724         this.buttons.push(btn);
48725         return btn;
48726     },
48727
48728      /**
48729      * Adds a series of form elements (using the xtype property as the factory method.
48730      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48731      * @param {Object} config 
48732      */
48733     
48734     addxtype : function()
48735     {
48736         var ar = Array.prototype.slice.call(arguments, 0);
48737         var ret = false;
48738         for(var i = 0; i < ar.length; i++) {
48739             if (!ar[i]) {
48740                 continue; // skip -- if this happends something invalid got sent, we 
48741                 // should ignore it, as basically that interface element will not show up
48742                 // and that should be pretty obvious!!
48743             }
48744             
48745             if (Roo.form[ar[i].xtype]) {
48746                 ar[i].form = this;
48747                 var fe = Roo.factory(ar[i], Roo.form);
48748                 if (!ret) {
48749                     ret = fe;
48750                 }
48751                 fe.form = this;
48752                 if (fe.store) {
48753                     fe.store.form = this;
48754                 }
48755                 if (fe.isLayout) {  
48756                          
48757                     this.start(fe);
48758                     this.allItems.push(fe);
48759                     if (fe.items && fe.addxtype) {
48760                         fe.addxtype.apply(fe, fe.items);
48761                         delete fe.items;
48762                     }
48763                      this.end();
48764                     continue;
48765                 }
48766                 
48767                 
48768                  
48769                 this.add(fe);
48770               //  console.log('adding ' + ar[i].xtype);
48771             }
48772             if (ar[i].xtype == 'Button') {  
48773                 //console.log('adding button');
48774                 //console.log(ar[i]);
48775                 this.addButton(ar[i]);
48776                 this.allItems.push(fe);
48777                 continue;
48778             }
48779             
48780             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48781                 alert('end is not supported on xtype any more, use items');
48782             //    this.end();
48783             //    //console.log('adding end');
48784             }
48785             
48786         }
48787         return ret;
48788     },
48789     
48790     /**
48791      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48792      * option "monitorValid"
48793      */
48794     startMonitoring : function(){
48795         if(!this.bound){
48796             this.bound = true;
48797             Roo.TaskMgr.start({
48798                 run : this.bindHandler,
48799                 interval : this.monitorPoll || 200,
48800                 scope: this
48801             });
48802         }
48803     },
48804
48805     /**
48806      * Stops monitoring of the valid state of this form
48807      */
48808     stopMonitoring : function(){
48809         this.bound = false;
48810     },
48811
48812     // private
48813     bindHandler : function(){
48814         if(!this.bound){
48815             return false; // stops binding
48816         }
48817         var valid = true;
48818         this.items.each(function(f){
48819             if(!f.isValid(true)){
48820                 valid = false;
48821                 return false;
48822             }
48823         });
48824         for(var i = 0, len = this.buttons.length; i < len; i++){
48825             var btn = this.buttons[i];
48826             if(btn.formBind === true && btn.disabled === valid){
48827                 btn.setDisabled(!valid);
48828             }
48829         }
48830         this.fireEvent('clientvalidation', this, valid);
48831     }
48832     
48833     
48834     
48835     
48836     
48837     
48838     
48839     
48840 });
48841
48842
48843 // back compat
48844 Roo.Form = Roo.form.Form;
48845 /*
48846  * Based on:
48847  * Ext JS Library 1.1.1
48848  * Copyright(c) 2006-2007, Ext JS, LLC.
48849  *
48850  * Originally Released Under LGPL - original licence link has changed is not relivant.
48851  *
48852  * Fork - LGPL
48853  * <script type="text/javascript">
48854  */
48855
48856 // as we use this in bootstrap.
48857 Roo.namespace('Roo.form');
48858  /**
48859  * @class Roo.form.Action
48860  * Internal Class used to handle form actions
48861  * @constructor
48862  * @param {Roo.form.BasicForm} el The form element or its id
48863  * @param {Object} config Configuration options
48864  */
48865
48866  
48867  
48868 // define the action interface
48869 Roo.form.Action = function(form, options){
48870     this.form = form;
48871     this.options = options || {};
48872 };
48873 /**
48874  * Client Validation Failed
48875  * @const 
48876  */
48877 Roo.form.Action.CLIENT_INVALID = 'client';
48878 /**
48879  * Server Validation Failed
48880  * @const 
48881  */
48882 Roo.form.Action.SERVER_INVALID = 'server';
48883  /**
48884  * Connect to Server Failed
48885  * @const 
48886  */
48887 Roo.form.Action.CONNECT_FAILURE = 'connect';
48888 /**
48889  * Reading Data from Server Failed
48890  * @const 
48891  */
48892 Roo.form.Action.LOAD_FAILURE = 'load';
48893
48894 Roo.form.Action.prototype = {
48895     type : 'default',
48896     failureType : undefined,
48897     response : undefined,
48898     result : undefined,
48899
48900     // interface method
48901     run : function(options){
48902
48903     },
48904
48905     // interface method
48906     success : function(response){
48907
48908     },
48909
48910     // interface method
48911     handleResponse : function(response){
48912
48913     },
48914
48915     // default connection failure
48916     failure : function(response){
48917         
48918         this.response = response;
48919         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48920         this.form.afterAction(this, false);
48921     },
48922
48923     processResponse : function(response){
48924         this.response = response;
48925         if(!response.responseText){
48926             return true;
48927         }
48928         this.result = this.handleResponse(response);
48929         return this.result;
48930     },
48931
48932     // utility functions used internally
48933     getUrl : function(appendParams){
48934         var url = this.options.url || this.form.url || this.form.el.dom.action;
48935         if(appendParams){
48936             var p = this.getParams();
48937             if(p){
48938                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48939             }
48940         }
48941         return url;
48942     },
48943
48944     getMethod : function(){
48945         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
48946     },
48947
48948     getParams : function(){
48949         var bp = this.form.baseParams;
48950         var p = this.options.params;
48951         if(p){
48952             if(typeof p == "object"){
48953                 p = Roo.urlEncode(Roo.applyIf(p, bp));
48954             }else if(typeof p == 'string' && bp){
48955                 p += '&' + Roo.urlEncode(bp);
48956             }
48957         }else if(bp){
48958             p = Roo.urlEncode(bp);
48959         }
48960         return p;
48961     },
48962
48963     createCallback : function(){
48964         return {
48965             success: this.success,
48966             failure: this.failure,
48967             scope: this,
48968             timeout: (this.form.timeout*1000),
48969             upload: this.form.fileUpload ? this.success : undefined
48970         };
48971     }
48972 };
48973
48974 Roo.form.Action.Submit = function(form, options){
48975     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
48976 };
48977
48978 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
48979     type : 'submit',
48980
48981     haveProgress : false,
48982     uploadComplete : false,
48983     
48984     // uploadProgress indicator.
48985     uploadProgress : function()
48986     {
48987         if (!this.form.progressUrl) {
48988             return;
48989         }
48990         
48991         if (!this.haveProgress) {
48992             Roo.MessageBox.progress("Uploading", "Uploading");
48993         }
48994         if (this.uploadComplete) {
48995            Roo.MessageBox.hide();
48996            return;
48997         }
48998         
48999         this.haveProgress = true;
49000    
49001         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49002         
49003         var c = new Roo.data.Connection();
49004         c.request({
49005             url : this.form.progressUrl,
49006             params: {
49007                 id : uid
49008             },
49009             method: 'GET',
49010             success : function(req){
49011                //console.log(data);
49012                 var rdata = false;
49013                 var edata;
49014                 try  {
49015                    rdata = Roo.decode(req.responseText)
49016                 } catch (e) {
49017                     Roo.log("Invalid data from server..");
49018                     Roo.log(edata);
49019                     return;
49020                 }
49021                 if (!rdata || !rdata.success) {
49022                     Roo.log(rdata);
49023                     Roo.MessageBox.alert(Roo.encode(rdata));
49024                     return;
49025                 }
49026                 var data = rdata.data;
49027                 
49028                 if (this.uploadComplete) {
49029                    Roo.MessageBox.hide();
49030                    return;
49031                 }
49032                    
49033                 if (data){
49034                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49035                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49036                     );
49037                 }
49038                 this.uploadProgress.defer(2000,this);
49039             },
49040        
49041             failure: function(data) {
49042                 Roo.log('progress url failed ');
49043                 Roo.log(data);
49044             },
49045             scope : this
49046         });
49047            
49048     },
49049     
49050     
49051     run : function()
49052     {
49053         // run get Values on the form, so it syncs any secondary forms.
49054         this.form.getValues();
49055         
49056         var o = this.options;
49057         var method = this.getMethod();
49058         var isPost = method == 'POST';
49059         if(o.clientValidation === false || this.form.isValid()){
49060             
49061             if (this.form.progressUrl) {
49062                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49063                     (new Date() * 1) + '' + Math.random());
49064                     
49065             } 
49066             
49067             
49068             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49069                 form:this.form.el.dom,
49070                 url:this.getUrl(!isPost),
49071                 method: method,
49072                 params:isPost ? this.getParams() : null,
49073                 isUpload: this.form.fileUpload,
49074                 formData : this.form.formData
49075             }));
49076             
49077             this.uploadProgress();
49078
49079         }else if (o.clientValidation !== false){ // client validation failed
49080             this.failureType = Roo.form.Action.CLIENT_INVALID;
49081             this.form.afterAction(this, false);
49082         }
49083     },
49084
49085     success : function(response)
49086     {
49087         this.uploadComplete= true;
49088         if (this.haveProgress) {
49089             Roo.MessageBox.hide();
49090         }
49091         
49092         
49093         var result = this.processResponse(response);
49094         if(result === true || result.success){
49095             this.form.afterAction(this, true);
49096             return;
49097         }
49098         if(result.errors){
49099             this.form.markInvalid(result.errors);
49100             this.failureType = Roo.form.Action.SERVER_INVALID;
49101         }
49102         this.form.afterAction(this, false);
49103     },
49104     failure : function(response)
49105     {
49106         this.uploadComplete= true;
49107         if (this.haveProgress) {
49108             Roo.MessageBox.hide();
49109         }
49110         
49111         this.response = response;
49112         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49113         this.form.afterAction(this, false);
49114     },
49115     
49116     handleResponse : function(response){
49117         if(this.form.errorReader){
49118             var rs = this.form.errorReader.read(response);
49119             var errors = [];
49120             if(rs.records){
49121                 for(var i = 0, len = rs.records.length; i < len; i++) {
49122                     var r = rs.records[i];
49123                     errors[i] = r.data;
49124                 }
49125             }
49126             if(errors.length < 1){
49127                 errors = null;
49128             }
49129             return {
49130                 success : rs.success,
49131                 errors : errors
49132             };
49133         }
49134         var ret = false;
49135         try {
49136             ret = Roo.decode(response.responseText);
49137         } catch (e) {
49138             ret = {
49139                 success: false,
49140                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49141                 errors : []
49142             };
49143         }
49144         return ret;
49145         
49146     }
49147 });
49148
49149
49150 Roo.form.Action.Load = function(form, options){
49151     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49152     this.reader = this.form.reader;
49153 };
49154
49155 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49156     type : 'load',
49157
49158     run : function(){
49159         
49160         Roo.Ajax.request(Roo.apply(
49161                 this.createCallback(), {
49162                     method:this.getMethod(),
49163                     url:this.getUrl(false),
49164                     params:this.getParams()
49165         }));
49166     },
49167
49168     success : function(response){
49169         
49170         var result = this.processResponse(response);
49171         if(result === true || !result.success || !result.data){
49172             this.failureType = Roo.form.Action.LOAD_FAILURE;
49173             this.form.afterAction(this, false);
49174             return;
49175         }
49176         this.form.clearInvalid();
49177         this.form.setValues(result.data);
49178         this.form.afterAction(this, true);
49179     },
49180
49181     handleResponse : function(response){
49182         if(this.form.reader){
49183             var rs = this.form.reader.read(response);
49184             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49185             return {
49186                 success : rs.success,
49187                 data : data
49188             };
49189         }
49190         return Roo.decode(response.responseText);
49191     }
49192 });
49193
49194 Roo.form.Action.ACTION_TYPES = {
49195     'load' : Roo.form.Action.Load,
49196     'submit' : Roo.form.Action.Submit
49197 };/*
49198  * Based on:
49199  * Ext JS Library 1.1.1
49200  * Copyright(c) 2006-2007, Ext JS, LLC.
49201  *
49202  * Originally Released Under LGPL - original licence link has changed is not relivant.
49203  *
49204  * Fork - LGPL
49205  * <script type="text/javascript">
49206  */
49207  
49208 /**
49209  * @class Roo.form.Layout
49210  * @extends Roo.Component
49211  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49212  * @constructor
49213  * @param {Object} config Configuration options
49214  */
49215 Roo.form.Layout = function(config){
49216     var xitems = [];
49217     if (config.items) {
49218         xitems = config.items;
49219         delete config.items;
49220     }
49221     Roo.form.Layout.superclass.constructor.call(this, config);
49222     this.stack = [];
49223     Roo.each(xitems, this.addxtype, this);
49224      
49225 };
49226
49227 Roo.extend(Roo.form.Layout, Roo.Component, {
49228     /**
49229      * @cfg {String/Object} autoCreate
49230      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49231      */
49232     /**
49233      * @cfg {String/Object/Function} style
49234      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49235      * a function which returns such a specification.
49236      */
49237     /**
49238      * @cfg {String} labelAlign
49239      * Valid values are "left," "top" and "right" (defaults to "left")
49240      */
49241     /**
49242      * @cfg {Number} labelWidth
49243      * Fixed width in pixels of all field labels (defaults to undefined)
49244      */
49245     /**
49246      * @cfg {Boolean} clear
49247      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49248      */
49249     clear : true,
49250     /**
49251      * @cfg {String} labelSeparator
49252      * The separator to use after field labels (defaults to ':')
49253      */
49254     labelSeparator : ':',
49255     /**
49256      * @cfg {Boolean} hideLabels
49257      * True to suppress the display of field labels in this layout (defaults to false)
49258      */
49259     hideLabels : false,
49260
49261     // private
49262     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49263     
49264     isLayout : true,
49265     
49266     // private
49267     onRender : function(ct, position){
49268         if(this.el){ // from markup
49269             this.el = Roo.get(this.el);
49270         }else {  // generate
49271             var cfg = this.getAutoCreate();
49272             this.el = ct.createChild(cfg, position);
49273         }
49274         if(this.style){
49275             this.el.applyStyles(this.style);
49276         }
49277         if(this.labelAlign){
49278             this.el.addClass('x-form-label-'+this.labelAlign);
49279         }
49280         if(this.hideLabels){
49281             this.labelStyle = "display:none";
49282             this.elementStyle = "padding-left:0;";
49283         }else{
49284             if(typeof this.labelWidth == 'number'){
49285                 this.labelStyle = "width:"+this.labelWidth+"px;";
49286                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49287             }
49288             if(this.labelAlign == 'top'){
49289                 this.labelStyle = "width:auto;";
49290                 this.elementStyle = "padding-left:0;";
49291             }
49292         }
49293         var stack = this.stack;
49294         var slen = stack.length;
49295         if(slen > 0){
49296             if(!this.fieldTpl){
49297                 var t = new Roo.Template(
49298                     '<div class="x-form-item {5}">',
49299                         '<label for="{0}" style="{2}">{1}{4}</label>',
49300                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49301                         '</div>',
49302                     '</div><div class="x-form-clear-left"></div>'
49303                 );
49304                 t.disableFormats = true;
49305                 t.compile();
49306                 Roo.form.Layout.prototype.fieldTpl = t;
49307             }
49308             for(var i = 0; i < slen; i++) {
49309                 if(stack[i].isFormField){
49310                     this.renderField(stack[i]);
49311                 }else{
49312                     this.renderComponent(stack[i]);
49313                 }
49314             }
49315         }
49316         if(this.clear){
49317             this.el.createChild({cls:'x-form-clear'});
49318         }
49319     },
49320
49321     // private
49322     renderField : function(f){
49323         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49324                f.id, //0
49325                f.fieldLabel, //1
49326                f.labelStyle||this.labelStyle||'', //2
49327                this.elementStyle||'', //3
49328                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49329                f.itemCls||this.itemCls||''  //5
49330        ], true).getPrevSibling());
49331     },
49332
49333     // private
49334     renderComponent : function(c){
49335         c.render(c.isLayout ? this.el : this.el.createChild());    
49336     },
49337     /**
49338      * Adds a object form elements (using the xtype property as the factory method.)
49339      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49340      * @param {Object} config 
49341      */
49342     addxtype : function(o)
49343     {
49344         // create the lement.
49345         o.form = this.form;
49346         var fe = Roo.factory(o, Roo.form);
49347         this.form.allItems.push(fe);
49348         this.stack.push(fe);
49349         
49350         if (fe.isFormField) {
49351             this.form.items.add(fe);
49352         }
49353          
49354         return fe;
49355     }
49356 });
49357
49358 /**
49359  * @class Roo.form.Column
49360  * @extends Roo.form.Layout
49361  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49362  * @constructor
49363  * @param {Object} config Configuration options
49364  */
49365 Roo.form.Column = function(config){
49366     Roo.form.Column.superclass.constructor.call(this, config);
49367 };
49368
49369 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49370     /**
49371      * @cfg {Number/String} width
49372      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49373      */
49374     /**
49375      * @cfg {String/Object} autoCreate
49376      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49377      */
49378
49379     // private
49380     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49381
49382     // private
49383     onRender : function(ct, position){
49384         Roo.form.Column.superclass.onRender.call(this, ct, position);
49385         if(this.width){
49386             this.el.setWidth(this.width);
49387         }
49388     }
49389 });
49390
49391
49392 /**
49393  * @class Roo.form.Row
49394  * @extends Roo.form.Layout
49395  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49396  * @constructor
49397  * @param {Object} config Configuration options
49398  */
49399
49400  
49401 Roo.form.Row = function(config){
49402     Roo.form.Row.superclass.constructor.call(this, config);
49403 };
49404  
49405 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49406       /**
49407      * @cfg {Number/String} width
49408      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49409      */
49410     /**
49411      * @cfg {Number/String} height
49412      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49413      */
49414     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49415     
49416     padWidth : 20,
49417     // private
49418     onRender : function(ct, position){
49419         //console.log('row render');
49420         if(!this.rowTpl){
49421             var t = new Roo.Template(
49422                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49423                     '<label for="{0}" style="{2}">{1}{4}</label>',
49424                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49425                     '</div>',
49426                 '</div>'
49427             );
49428             t.disableFormats = true;
49429             t.compile();
49430             Roo.form.Layout.prototype.rowTpl = t;
49431         }
49432         this.fieldTpl = this.rowTpl;
49433         
49434         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49435         var labelWidth = 100;
49436         
49437         if ((this.labelAlign != 'top')) {
49438             if (typeof this.labelWidth == 'number') {
49439                 labelWidth = this.labelWidth
49440             }
49441             this.padWidth =  20 + labelWidth;
49442             
49443         }
49444         
49445         Roo.form.Column.superclass.onRender.call(this, ct, position);
49446         if(this.width){
49447             this.el.setWidth(this.width);
49448         }
49449         if(this.height){
49450             this.el.setHeight(this.height);
49451         }
49452     },
49453     
49454     // private
49455     renderField : function(f){
49456         f.fieldEl = this.fieldTpl.append(this.el, [
49457                f.id, f.fieldLabel,
49458                f.labelStyle||this.labelStyle||'',
49459                this.elementStyle||'',
49460                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49461                f.itemCls||this.itemCls||'',
49462                f.width ? f.width + this.padWidth : 160 + this.padWidth
49463        ],true);
49464     }
49465 });
49466  
49467
49468 /**
49469  * @class Roo.form.FieldSet
49470  * @extends Roo.form.Layout
49471  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49472  * @constructor
49473  * @param {Object} config Configuration options
49474  */
49475 Roo.form.FieldSet = function(config){
49476     Roo.form.FieldSet.superclass.constructor.call(this, config);
49477 };
49478
49479 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49480     /**
49481      * @cfg {String} legend
49482      * The text to display as the legend for the FieldSet (defaults to '')
49483      */
49484     /**
49485      * @cfg {String/Object} autoCreate
49486      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49487      */
49488
49489     // private
49490     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49491
49492     // private
49493     onRender : function(ct, position){
49494         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49495         if(this.legend){
49496             this.setLegend(this.legend);
49497         }
49498     },
49499
49500     // private
49501     setLegend : function(text){
49502         if(this.rendered){
49503             this.el.child('legend').update(text);
49504         }
49505     }
49506 });/*
49507  * Based on:
49508  * Ext JS Library 1.1.1
49509  * Copyright(c) 2006-2007, Ext JS, LLC.
49510  *
49511  * Originally Released Under LGPL - original licence link has changed is not relivant.
49512  *
49513  * Fork - LGPL
49514  * <script type="text/javascript">
49515  */
49516 /**
49517  * @class Roo.form.VTypes
49518  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49519  * @singleton
49520  */
49521 Roo.form.VTypes = function(){
49522     // closure these in so they are only created once.
49523     var alpha = /^[a-zA-Z_]+$/;
49524     var alphanum = /^[a-zA-Z0-9_]+$/;
49525     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49526     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49527
49528     // All these messages and functions are configurable
49529     return {
49530         /**
49531          * The function used to validate email addresses
49532          * @param {String} value The email address
49533          */
49534         'email' : function(v){
49535             return email.test(v);
49536         },
49537         /**
49538          * The error text to display when the email validation function returns false
49539          * @type String
49540          */
49541         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49542         /**
49543          * The keystroke filter mask to be applied on email input
49544          * @type RegExp
49545          */
49546         'emailMask' : /[a-z0-9_\.\-@]/i,
49547
49548         /**
49549          * The function used to validate URLs
49550          * @param {String} value The URL
49551          */
49552         'url' : function(v){
49553             return url.test(v);
49554         },
49555         /**
49556          * The error text to display when the url validation function returns false
49557          * @type String
49558          */
49559         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49560         
49561         /**
49562          * The function used to validate alpha values
49563          * @param {String} value The value
49564          */
49565         'alpha' : function(v){
49566             return alpha.test(v);
49567         },
49568         /**
49569          * The error text to display when the alpha validation function returns false
49570          * @type String
49571          */
49572         'alphaText' : 'This field should only contain letters and _',
49573         /**
49574          * The keystroke filter mask to be applied on alpha input
49575          * @type RegExp
49576          */
49577         'alphaMask' : /[a-z_]/i,
49578
49579         /**
49580          * The function used to validate alphanumeric values
49581          * @param {String} value The value
49582          */
49583         'alphanum' : function(v){
49584             return alphanum.test(v);
49585         },
49586         /**
49587          * The error text to display when the alphanumeric validation function returns false
49588          * @type String
49589          */
49590         'alphanumText' : 'This field should only contain letters, numbers and _',
49591         /**
49592          * The keystroke filter mask to be applied on alphanumeric input
49593          * @type RegExp
49594          */
49595         'alphanumMask' : /[a-z0-9_]/i
49596     };
49597 }();//<script type="text/javascript">
49598
49599 /**
49600  * @class Roo.form.FCKeditor
49601  * @extends Roo.form.TextArea
49602  * Wrapper around the FCKEditor http://www.fckeditor.net
49603  * @constructor
49604  * Creates a new FCKeditor
49605  * @param {Object} config Configuration options
49606  */
49607 Roo.form.FCKeditor = function(config){
49608     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49609     this.addEvents({
49610          /**
49611          * @event editorinit
49612          * Fired when the editor is initialized - you can add extra handlers here..
49613          * @param {FCKeditor} this
49614          * @param {Object} the FCK object.
49615          */
49616         editorinit : true
49617     });
49618     
49619     
49620 };
49621 Roo.form.FCKeditor.editors = { };
49622 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49623 {
49624     //defaultAutoCreate : {
49625     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49626     //},
49627     // private
49628     /**
49629      * @cfg {Object} fck options - see fck manual for details.
49630      */
49631     fckconfig : false,
49632     
49633     /**
49634      * @cfg {Object} fck toolbar set (Basic or Default)
49635      */
49636     toolbarSet : 'Basic',
49637     /**
49638      * @cfg {Object} fck BasePath
49639      */ 
49640     basePath : '/fckeditor/',
49641     
49642     
49643     frame : false,
49644     
49645     value : '',
49646     
49647    
49648     onRender : function(ct, position)
49649     {
49650         if(!this.el){
49651             this.defaultAutoCreate = {
49652                 tag: "textarea",
49653                 style:"width:300px;height:60px;",
49654                 autocomplete: "new-password"
49655             };
49656         }
49657         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49658         /*
49659         if(this.grow){
49660             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49661             if(this.preventScrollbars){
49662                 this.el.setStyle("overflow", "hidden");
49663             }
49664             this.el.setHeight(this.growMin);
49665         }
49666         */
49667         //console.log('onrender' + this.getId() );
49668         Roo.form.FCKeditor.editors[this.getId()] = this;
49669          
49670
49671         this.replaceTextarea() ;
49672         
49673     },
49674     
49675     getEditor : function() {
49676         return this.fckEditor;
49677     },
49678     /**
49679      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49680      * @param {Mixed} value The value to set
49681      */
49682     
49683     
49684     setValue : function(value)
49685     {
49686         //console.log('setValue: ' + value);
49687         
49688         if(typeof(value) == 'undefined') { // not sure why this is happending...
49689             return;
49690         }
49691         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49692         
49693         //if(!this.el || !this.getEditor()) {
49694         //    this.value = value;
49695             //this.setValue.defer(100,this,[value]);    
49696         //    return;
49697         //} 
49698         
49699         if(!this.getEditor()) {
49700             return;
49701         }
49702         
49703         this.getEditor().SetData(value);
49704         
49705         //
49706
49707     },
49708
49709     /**
49710      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49711      * @return {Mixed} value The field value
49712      */
49713     getValue : function()
49714     {
49715         
49716         if (this.frame && this.frame.dom.style.display == 'none') {
49717             return Roo.form.FCKeditor.superclass.getValue.call(this);
49718         }
49719         
49720         if(!this.el || !this.getEditor()) {
49721            
49722            // this.getValue.defer(100,this); 
49723             return this.value;
49724         }
49725        
49726         
49727         var value=this.getEditor().GetData();
49728         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49729         return Roo.form.FCKeditor.superclass.getValue.call(this);
49730         
49731
49732     },
49733
49734     /**
49735      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49736      * @return {Mixed} value The field value
49737      */
49738     getRawValue : function()
49739     {
49740         if (this.frame && this.frame.dom.style.display == 'none') {
49741             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49742         }
49743         
49744         if(!this.el || !this.getEditor()) {
49745             //this.getRawValue.defer(100,this); 
49746             return this.value;
49747             return;
49748         }
49749         
49750         
49751         
49752         var value=this.getEditor().GetData();
49753         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49754         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49755          
49756     },
49757     
49758     setSize : function(w,h) {
49759         
49760         
49761         
49762         //if (this.frame && this.frame.dom.style.display == 'none') {
49763         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49764         //    return;
49765         //}
49766         //if(!this.el || !this.getEditor()) {
49767         //    this.setSize.defer(100,this, [w,h]); 
49768         //    return;
49769         //}
49770         
49771         
49772         
49773         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49774         
49775         this.frame.dom.setAttribute('width', w);
49776         this.frame.dom.setAttribute('height', h);
49777         this.frame.setSize(w,h);
49778         
49779     },
49780     
49781     toggleSourceEdit : function(value) {
49782         
49783       
49784          
49785         this.el.dom.style.display = value ? '' : 'none';
49786         this.frame.dom.style.display = value ?  'none' : '';
49787         
49788     },
49789     
49790     
49791     focus: function(tag)
49792     {
49793         if (this.frame.dom.style.display == 'none') {
49794             return Roo.form.FCKeditor.superclass.focus.call(this);
49795         }
49796         if(!this.el || !this.getEditor()) {
49797             this.focus.defer(100,this, [tag]); 
49798             return;
49799         }
49800         
49801         
49802         
49803         
49804         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49805         this.getEditor().Focus();
49806         if (tgs.length) {
49807             if (!this.getEditor().Selection.GetSelection()) {
49808                 this.focus.defer(100,this, [tag]); 
49809                 return;
49810             }
49811             
49812             
49813             var r = this.getEditor().EditorDocument.createRange();
49814             r.setStart(tgs[0],0);
49815             r.setEnd(tgs[0],0);
49816             this.getEditor().Selection.GetSelection().removeAllRanges();
49817             this.getEditor().Selection.GetSelection().addRange(r);
49818             this.getEditor().Focus();
49819         }
49820         
49821     },
49822     
49823     
49824     
49825     replaceTextarea : function()
49826     {
49827         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49828             return ;
49829         }
49830         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49831         //{
49832             // We must check the elements firstly using the Id and then the name.
49833         var oTextarea = document.getElementById( this.getId() );
49834         
49835         var colElementsByName = document.getElementsByName( this.getId() ) ;
49836          
49837         oTextarea.style.display = 'none' ;
49838
49839         if ( oTextarea.tabIndex ) {            
49840             this.TabIndex = oTextarea.tabIndex ;
49841         }
49842         
49843         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49844         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49845         this.frame = Roo.get(this.getId() + '___Frame')
49846     },
49847     
49848     _getConfigHtml : function()
49849     {
49850         var sConfig = '' ;
49851
49852         for ( var o in this.fckconfig ) {
49853             sConfig += sConfig.length > 0  ? '&amp;' : '';
49854             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49855         }
49856
49857         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49858     },
49859     
49860     
49861     _getIFrameHtml : function()
49862     {
49863         var sFile = 'fckeditor.html' ;
49864         /* no idea what this is about..
49865         try
49866         {
49867             if ( (/fcksource=true/i).test( window.top.location.search ) )
49868                 sFile = 'fckeditor.original.html' ;
49869         }
49870         catch (e) { 
49871         */
49872
49873         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49874         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49875         
49876         
49877         var html = '<iframe id="' + this.getId() +
49878             '___Frame" src="' + sLink +
49879             '" width="' + this.width +
49880             '" height="' + this.height + '"' +
49881             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49882             ' frameborder="0" scrolling="no"></iframe>' ;
49883
49884         return html ;
49885     },
49886     
49887     _insertHtmlBefore : function( html, element )
49888     {
49889         if ( element.insertAdjacentHTML )       {
49890             // IE
49891             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49892         } else { // Gecko
49893             var oRange = document.createRange() ;
49894             oRange.setStartBefore( element ) ;
49895             var oFragment = oRange.createContextualFragment( html );
49896             element.parentNode.insertBefore( oFragment, element ) ;
49897         }
49898     }
49899     
49900     
49901   
49902     
49903     
49904     
49905     
49906
49907 });
49908
49909 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49910
49911 function FCKeditor_OnComplete(editorInstance){
49912     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49913     f.fckEditor = editorInstance;
49914     //console.log("loaded");
49915     f.fireEvent('editorinit', f, editorInstance);
49916
49917   
49918
49919  
49920
49921
49922
49923
49924
49925
49926
49927
49928
49929
49930
49931
49932
49933
49934
49935 //<script type="text/javascript">
49936 /**
49937  * @class Roo.form.GridField
49938  * @extends Roo.form.Field
49939  * Embed a grid (or editable grid into a form)
49940  * STATUS ALPHA
49941  * 
49942  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
49943  * it needs 
49944  * xgrid.store = Roo.data.Store
49945  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
49946  * xgrid.store.reader = Roo.data.JsonReader 
49947  * 
49948  * 
49949  * @constructor
49950  * Creates a new GridField
49951  * @param {Object} config Configuration options
49952  */
49953 Roo.form.GridField = function(config){
49954     Roo.form.GridField.superclass.constructor.call(this, config);
49955      
49956 };
49957
49958 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
49959     /**
49960      * @cfg {Number} width  - used to restrict width of grid..
49961      */
49962     width : 100,
49963     /**
49964      * @cfg {Number} height - used to restrict height of grid..
49965      */
49966     height : 50,
49967      /**
49968      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
49969          * 
49970          *}
49971      */
49972     xgrid : false, 
49973     /**
49974      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
49975      * {tag: "input", type: "checkbox", autocomplete: "off"})
49976      */
49977    // defaultAutoCreate : { tag: 'div' },
49978     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
49979     /**
49980      * @cfg {String} addTitle Text to include for adding a title.
49981      */
49982     addTitle : false,
49983     //
49984     onResize : function(){
49985         Roo.form.Field.superclass.onResize.apply(this, arguments);
49986     },
49987
49988     initEvents : function(){
49989         // Roo.form.Checkbox.superclass.initEvents.call(this);
49990         // has no events...
49991        
49992     },
49993
49994
49995     getResizeEl : function(){
49996         return this.wrap;
49997     },
49998
49999     getPositionEl : function(){
50000         return this.wrap;
50001     },
50002
50003     // private
50004     onRender : function(ct, position){
50005         
50006         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50007         var style = this.style;
50008         delete this.style;
50009         
50010         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50011         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50012         this.viewEl = this.wrap.createChild({ tag: 'div' });
50013         if (style) {
50014             this.viewEl.applyStyles(style);
50015         }
50016         if (this.width) {
50017             this.viewEl.setWidth(this.width);
50018         }
50019         if (this.height) {
50020             this.viewEl.setHeight(this.height);
50021         }
50022         //if(this.inputValue !== undefined){
50023         //this.setValue(this.value);
50024         
50025         
50026         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50027         
50028         
50029         this.grid.render();
50030         this.grid.getDataSource().on('remove', this.refreshValue, this);
50031         this.grid.getDataSource().on('update', this.refreshValue, this);
50032         this.grid.on('afteredit', this.refreshValue, this);
50033  
50034     },
50035      
50036     
50037     /**
50038      * Sets the value of the item. 
50039      * @param {String} either an object  or a string..
50040      */
50041     setValue : function(v){
50042         //this.value = v;
50043         v = v || []; // empty set..
50044         // this does not seem smart - it really only affects memoryproxy grids..
50045         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50046             var ds = this.grid.getDataSource();
50047             // assumes a json reader..
50048             var data = {}
50049             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50050             ds.loadData( data);
50051         }
50052         // clear selection so it does not get stale.
50053         if (this.grid.sm) { 
50054             this.grid.sm.clearSelections();
50055         }
50056         
50057         Roo.form.GridField.superclass.setValue.call(this, v);
50058         this.refreshValue();
50059         // should load data in the grid really....
50060     },
50061     
50062     // private
50063     refreshValue: function() {
50064          var val = [];
50065         this.grid.getDataSource().each(function(r) {
50066             val.push(r.data);
50067         });
50068         this.el.dom.value = Roo.encode(val);
50069     }
50070     
50071      
50072     
50073     
50074 });/*
50075  * Based on:
50076  * Ext JS Library 1.1.1
50077  * Copyright(c) 2006-2007, Ext JS, LLC.
50078  *
50079  * Originally Released Under LGPL - original licence link has changed is not relivant.
50080  *
50081  * Fork - LGPL
50082  * <script type="text/javascript">
50083  */
50084 /**
50085  * @class Roo.form.DisplayField
50086  * @extends Roo.form.Field
50087  * A generic Field to display non-editable data.
50088  * @cfg {Boolean} closable (true|false) default false
50089  * @constructor
50090  * Creates a new Display Field item.
50091  * @param {Object} config Configuration options
50092  */
50093 Roo.form.DisplayField = function(config){
50094     Roo.form.DisplayField.superclass.constructor.call(this, config);
50095     
50096     this.addEvents({
50097         /**
50098          * @event close
50099          * Fires after the click the close btn
50100              * @param {Roo.form.DisplayField} this
50101              */
50102         close : true
50103     });
50104 };
50105
50106 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50107     inputType:      'hidden',
50108     allowBlank:     true,
50109     readOnly:         true,
50110     
50111  
50112     /**
50113      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50114      */
50115     focusClass : undefined,
50116     /**
50117      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50118      */
50119     fieldClass: 'x-form-field',
50120     
50121      /**
50122      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50123      */
50124     valueRenderer: undefined,
50125     
50126     width: 100,
50127     /**
50128      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50129      * {tag: "input", type: "checkbox", autocomplete: "off"})
50130      */
50131      
50132  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50133  
50134     closable : false,
50135     
50136     onResize : function(){
50137         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50138         
50139     },
50140
50141     initEvents : function(){
50142         // Roo.form.Checkbox.superclass.initEvents.call(this);
50143         // has no events...
50144         
50145         if(this.closable){
50146             this.closeEl.on('click', this.onClose, this);
50147         }
50148        
50149     },
50150
50151
50152     getResizeEl : function(){
50153         return this.wrap;
50154     },
50155
50156     getPositionEl : function(){
50157         return this.wrap;
50158     },
50159
50160     // private
50161     onRender : function(ct, position){
50162         
50163         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50164         //if(this.inputValue !== undefined){
50165         this.wrap = this.el.wrap();
50166         
50167         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50168         
50169         if(this.closable){
50170             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50171         }
50172         
50173         if (this.bodyStyle) {
50174             this.viewEl.applyStyles(this.bodyStyle);
50175         }
50176         //this.viewEl.setStyle('padding', '2px');
50177         
50178         this.setValue(this.value);
50179         
50180     },
50181 /*
50182     // private
50183     initValue : Roo.emptyFn,
50184
50185   */
50186
50187         // private
50188     onClick : function(){
50189         
50190     },
50191
50192     /**
50193      * Sets the checked state of the checkbox.
50194      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50195      */
50196     setValue : function(v){
50197         this.value = v;
50198         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50199         // this might be called before we have a dom element..
50200         if (!this.viewEl) {
50201             return;
50202         }
50203         this.viewEl.dom.innerHTML = html;
50204         Roo.form.DisplayField.superclass.setValue.call(this, v);
50205
50206     },
50207     
50208     onClose : function(e)
50209     {
50210         e.preventDefault();
50211         
50212         this.fireEvent('close', this);
50213     }
50214 });/*
50215  * 
50216  * Licence- LGPL
50217  * 
50218  */
50219
50220 /**
50221  * @class Roo.form.DayPicker
50222  * @extends Roo.form.Field
50223  * A Day picker show [M] [T] [W] ....
50224  * @constructor
50225  * Creates a new Day Picker
50226  * @param {Object} config Configuration options
50227  */
50228 Roo.form.DayPicker= function(config){
50229     Roo.form.DayPicker.superclass.constructor.call(this, config);
50230      
50231 };
50232
50233 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50234     /**
50235      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50236      */
50237     focusClass : undefined,
50238     /**
50239      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50240      */
50241     fieldClass: "x-form-field",
50242    
50243     /**
50244      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50245      * {tag: "input", type: "checkbox", autocomplete: "off"})
50246      */
50247     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50248     
50249    
50250     actionMode : 'viewEl', 
50251     //
50252     // private
50253  
50254     inputType : 'hidden',
50255     
50256      
50257     inputElement: false, // real input element?
50258     basedOn: false, // ????
50259     
50260     isFormField: true, // not sure where this is needed!!!!
50261
50262     onResize : function(){
50263         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50264         if(!this.boxLabel){
50265             this.el.alignTo(this.wrap, 'c-c');
50266         }
50267     },
50268
50269     initEvents : function(){
50270         Roo.form.Checkbox.superclass.initEvents.call(this);
50271         this.el.on("click", this.onClick,  this);
50272         this.el.on("change", this.onClick,  this);
50273     },
50274
50275
50276     getResizeEl : function(){
50277         return this.wrap;
50278     },
50279
50280     getPositionEl : function(){
50281         return this.wrap;
50282     },
50283
50284     
50285     // private
50286     onRender : function(ct, position){
50287         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50288        
50289         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50290         
50291         var r1 = '<table><tr>';
50292         var r2 = '<tr class="x-form-daypick-icons">';
50293         for (var i=0; i < 7; i++) {
50294             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50295             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50296         }
50297         
50298         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50299         viewEl.select('img').on('click', this.onClick, this);
50300         this.viewEl = viewEl;   
50301         
50302         
50303         // this will not work on Chrome!!!
50304         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50305         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50306         
50307         
50308           
50309
50310     },
50311
50312     // private
50313     initValue : Roo.emptyFn,
50314
50315     /**
50316      * Returns the checked state of the checkbox.
50317      * @return {Boolean} True if checked, else false
50318      */
50319     getValue : function(){
50320         return this.el.dom.value;
50321         
50322     },
50323
50324         // private
50325     onClick : function(e){ 
50326         //this.setChecked(!this.checked);
50327         Roo.get(e.target).toggleClass('x-menu-item-checked');
50328         this.refreshValue();
50329         //if(this.el.dom.checked != this.checked){
50330         //    this.setValue(this.el.dom.checked);
50331        // }
50332     },
50333     
50334     // private
50335     refreshValue : function()
50336     {
50337         var val = '';
50338         this.viewEl.select('img',true).each(function(e,i,n)  {
50339             val += e.is(".x-menu-item-checked") ? String(n) : '';
50340         });
50341         this.setValue(val, true);
50342     },
50343
50344     /**
50345      * Sets the checked state of the checkbox.
50346      * On is always based on a string comparison between inputValue and the param.
50347      * @param {Boolean/String} value - the value to set 
50348      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50349      */
50350     setValue : function(v,suppressEvent){
50351         if (!this.el.dom) {
50352             return;
50353         }
50354         var old = this.el.dom.value ;
50355         this.el.dom.value = v;
50356         if (suppressEvent) {
50357             return ;
50358         }
50359          
50360         // update display..
50361         this.viewEl.select('img',true).each(function(e,i,n)  {
50362             
50363             var on = e.is(".x-menu-item-checked");
50364             var newv = v.indexOf(String(n)) > -1;
50365             if (on != newv) {
50366                 e.toggleClass('x-menu-item-checked');
50367             }
50368             
50369         });
50370         
50371         
50372         this.fireEvent('change', this, v, old);
50373         
50374         
50375     },
50376    
50377     // handle setting of hidden value by some other method!!?!?
50378     setFromHidden: function()
50379     {
50380         if(!this.el){
50381             return;
50382         }
50383         //console.log("SET FROM HIDDEN");
50384         //alert('setFrom hidden');
50385         this.setValue(this.el.dom.value);
50386     },
50387     
50388     onDestroy : function()
50389     {
50390         if(this.viewEl){
50391             Roo.get(this.viewEl).remove();
50392         }
50393          
50394         Roo.form.DayPicker.superclass.onDestroy.call(this);
50395     }
50396
50397 });/*
50398  * RooJS Library 1.1.1
50399  * Copyright(c) 2008-2011  Alan Knowles
50400  *
50401  * License - LGPL
50402  */
50403  
50404
50405 /**
50406  * @class Roo.form.ComboCheck
50407  * @extends Roo.form.ComboBox
50408  * A combobox for multiple select items.
50409  *
50410  * FIXME - could do with a reset button..
50411  * 
50412  * @constructor
50413  * Create a new ComboCheck
50414  * @param {Object} config Configuration options
50415  */
50416 Roo.form.ComboCheck = function(config){
50417     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50418     // should verify some data...
50419     // like
50420     // hiddenName = required..
50421     // displayField = required
50422     // valudField == required
50423     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50424     var _t = this;
50425     Roo.each(req, function(e) {
50426         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50427             throw "Roo.form.ComboCheck : missing value for: " + e;
50428         }
50429     });
50430     
50431     
50432 };
50433
50434 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50435      
50436      
50437     editable : false,
50438      
50439     selectedClass: 'x-menu-item-checked', 
50440     
50441     // private
50442     onRender : function(ct, position){
50443         var _t = this;
50444         
50445         
50446         
50447         if(!this.tpl){
50448             var cls = 'x-combo-list';
50449
50450             
50451             this.tpl =  new Roo.Template({
50452                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50453                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50454                    '<span>{' + this.displayField + '}</span>' +
50455                     '</div>' 
50456                 
50457             });
50458         }
50459  
50460         
50461         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50462         this.view.singleSelect = false;
50463         this.view.multiSelect = true;
50464         this.view.toggleSelect = true;
50465         this.pageTb.add(new Roo.Toolbar.Fill(), {
50466             
50467             text: 'Done',
50468             handler: function()
50469             {
50470                 _t.collapse();
50471             }
50472         });
50473     },
50474     
50475     onViewOver : function(e, t){
50476         // do nothing...
50477         return;
50478         
50479     },
50480     
50481     onViewClick : function(doFocus,index){
50482         return;
50483         
50484     },
50485     select: function () {
50486         //Roo.log("SELECT CALLED");
50487     },
50488      
50489     selectByValue : function(xv, scrollIntoView){
50490         var ar = this.getValueArray();
50491         var sels = [];
50492         
50493         Roo.each(ar, function(v) {
50494             if(v === undefined || v === null){
50495                 return;
50496             }
50497             var r = this.findRecord(this.valueField, v);
50498             if(r){
50499                 sels.push(this.store.indexOf(r))
50500                 
50501             }
50502         },this);
50503         this.view.select(sels);
50504         return false;
50505     },
50506     
50507     
50508     
50509     onSelect : function(record, index){
50510        // Roo.log("onselect Called");
50511        // this is only called by the clear button now..
50512         this.view.clearSelections();
50513         this.setValue('[]');
50514         if (this.value != this.valueBefore) {
50515             this.fireEvent('change', this, this.value, this.valueBefore);
50516             this.valueBefore = this.value;
50517         }
50518     },
50519     getValueArray : function()
50520     {
50521         var ar = [] ;
50522         
50523         try {
50524             //Roo.log(this.value);
50525             if (typeof(this.value) == 'undefined') {
50526                 return [];
50527             }
50528             var ar = Roo.decode(this.value);
50529             return  ar instanceof Array ? ar : []; //?? valid?
50530             
50531         } catch(e) {
50532             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50533             return [];
50534         }
50535          
50536     },
50537     expand : function ()
50538     {
50539         
50540         Roo.form.ComboCheck.superclass.expand.call(this);
50541         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50542         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50543         
50544
50545     },
50546     
50547     collapse : function(){
50548         Roo.form.ComboCheck.superclass.collapse.call(this);
50549         var sl = this.view.getSelectedIndexes();
50550         var st = this.store;
50551         var nv = [];
50552         var tv = [];
50553         var r;
50554         Roo.each(sl, function(i) {
50555             r = st.getAt(i);
50556             nv.push(r.get(this.valueField));
50557         },this);
50558         this.setValue(Roo.encode(nv));
50559         if (this.value != this.valueBefore) {
50560
50561             this.fireEvent('change', this, this.value, this.valueBefore);
50562             this.valueBefore = this.value;
50563         }
50564         
50565     },
50566     
50567     setValue : function(v){
50568         // Roo.log(v);
50569         this.value = v;
50570         
50571         var vals = this.getValueArray();
50572         var tv = [];
50573         Roo.each(vals, function(k) {
50574             var r = this.findRecord(this.valueField, k);
50575             if(r){
50576                 tv.push(r.data[this.displayField]);
50577             }else if(this.valueNotFoundText !== undefined){
50578                 tv.push( this.valueNotFoundText );
50579             }
50580         },this);
50581        // Roo.log(tv);
50582         
50583         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50584         this.hiddenField.value = v;
50585         this.value = v;
50586     }
50587     
50588 });/*
50589  * Based on:
50590  * Ext JS Library 1.1.1
50591  * Copyright(c) 2006-2007, Ext JS, LLC.
50592  *
50593  * Originally Released Under LGPL - original licence link has changed is not relivant.
50594  *
50595  * Fork - LGPL
50596  * <script type="text/javascript">
50597  */
50598  
50599 /**
50600  * @class Roo.form.Signature
50601  * @extends Roo.form.Field
50602  * Signature field.  
50603  * @constructor
50604  * 
50605  * @param {Object} config Configuration options
50606  */
50607
50608 Roo.form.Signature = function(config){
50609     Roo.form.Signature.superclass.constructor.call(this, config);
50610     
50611     this.addEvents({// not in used??
50612          /**
50613          * @event confirm
50614          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50615              * @param {Roo.form.Signature} combo This combo box
50616              */
50617         'confirm' : true,
50618         /**
50619          * @event reset
50620          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50621              * @param {Roo.form.ComboBox} combo This combo box
50622              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50623              */
50624         'reset' : true
50625     });
50626 };
50627
50628 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50629     /**
50630      * @cfg {Object} labels Label to use when rendering a form.
50631      * defaults to 
50632      * labels : { 
50633      *      clear : "Clear",
50634      *      confirm : "Confirm"
50635      *  }
50636      */
50637     labels : { 
50638         clear : "Clear",
50639         confirm : "Confirm"
50640     },
50641     /**
50642      * @cfg {Number} width The signature panel width (defaults to 300)
50643      */
50644     width: 300,
50645     /**
50646      * @cfg {Number} height The signature panel height (defaults to 100)
50647      */
50648     height : 100,
50649     /**
50650      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50651      */
50652     allowBlank : false,
50653     
50654     //private
50655     // {Object} signPanel The signature SVG panel element (defaults to {})
50656     signPanel : {},
50657     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50658     isMouseDown : false,
50659     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50660     isConfirmed : false,
50661     // {String} signatureTmp SVG mapping string (defaults to empty string)
50662     signatureTmp : '',
50663     
50664     
50665     defaultAutoCreate : { // modified by initCompnoent..
50666         tag: "input",
50667         type:"hidden"
50668     },
50669
50670     // private
50671     onRender : function(ct, position){
50672         
50673         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50674         
50675         this.wrap = this.el.wrap({
50676             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50677         });
50678         
50679         this.createToolbar(this);
50680         this.signPanel = this.wrap.createChild({
50681                 tag: 'div',
50682                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50683             }, this.el
50684         );
50685             
50686         this.svgID = Roo.id();
50687         this.svgEl = this.signPanel.createChild({
50688               xmlns : 'http://www.w3.org/2000/svg',
50689               tag : 'svg',
50690               id : this.svgID + "-svg",
50691               width: this.width,
50692               height: this.height,
50693               viewBox: '0 0 '+this.width+' '+this.height,
50694               cn : [
50695                 {
50696                     tag: "rect",
50697                     id: this.svgID + "-svg-r",
50698                     width: this.width,
50699                     height: this.height,
50700                     fill: "#ffa"
50701                 },
50702                 {
50703                     tag: "line",
50704                     id: this.svgID + "-svg-l",
50705                     x1: "0", // start
50706                     y1: (this.height*0.8), // start set the line in 80% of height
50707                     x2: this.width, // end
50708                     y2: (this.height*0.8), // end set the line in 80% of height
50709                     'stroke': "#666",
50710                     'stroke-width': "1",
50711                     'stroke-dasharray': "3",
50712                     'shape-rendering': "crispEdges",
50713                     'pointer-events': "none"
50714                 },
50715                 {
50716                     tag: "path",
50717                     id: this.svgID + "-svg-p",
50718                     'stroke': "navy",
50719                     'stroke-width': "3",
50720                     'fill': "none",
50721                     'pointer-events': 'none'
50722                 }
50723               ]
50724         });
50725         this.createSVG();
50726         this.svgBox = this.svgEl.dom.getScreenCTM();
50727     },
50728     createSVG : function(){ 
50729         var svg = this.signPanel;
50730         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50731         var t = this;
50732
50733         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50734         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50735         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50736         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50737         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50738         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50739         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50740         
50741     },
50742     isTouchEvent : function(e){
50743         return e.type.match(/^touch/);
50744     },
50745     getCoords : function (e) {
50746         var pt    = this.svgEl.dom.createSVGPoint();
50747         pt.x = e.clientX; 
50748         pt.y = e.clientY;
50749         if (this.isTouchEvent(e)) {
50750             pt.x =  e.targetTouches[0].clientX;
50751             pt.y = e.targetTouches[0].clientY;
50752         }
50753         var a = this.svgEl.dom.getScreenCTM();
50754         var b = a.inverse();
50755         var mx = pt.matrixTransform(b);
50756         return mx.x + ',' + mx.y;
50757     },
50758     //mouse event headler 
50759     down : function (e) {
50760         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50761         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50762         
50763         this.isMouseDown = true;
50764         
50765         e.preventDefault();
50766     },
50767     move : function (e) {
50768         if (this.isMouseDown) {
50769             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50770             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50771         }
50772         
50773         e.preventDefault();
50774     },
50775     up : function (e) {
50776         this.isMouseDown = false;
50777         var sp = this.signatureTmp.split(' ');
50778         
50779         if(sp.length > 1){
50780             if(!sp[sp.length-2].match(/^L/)){
50781                 sp.pop();
50782                 sp.pop();
50783                 sp.push("");
50784                 this.signatureTmp = sp.join(" ");
50785             }
50786         }
50787         if(this.getValue() != this.signatureTmp){
50788             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50789             this.isConfirmed = false;
50790         }
50791         e.preventDefault();
50792     },
50793     
50794     /**
50795      * Protected method that will not generally be called directly. It
50796      * is called when the editor creates its toolbar. Override this method if you need to
50797      * add custom toolbar buttons.
50798      * @param {HtmlEditor} editor
50799      */
50800     createToolbar : function(editor){
50801          function btn(id, toggle, handler){
50802             var xid = fid + '-'+ id ;
50803             return {
50804                 id : xid,
50805                 cmd : id,
50806                 cls : 'x-btn-icon x-edit-'+id,
50807                 enableToggle:toggle !== false,
50808                 scope: editor, // was editor...
50809                 handler:handler||editor.relayBtnCmd,
50810                 clickEvent:'mousedown',
50811                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50812                 tabIndex:-1
50813             };
50814         }
50815         
50816         
50817         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50818         this.tb = tb;
50819         this.tb.add(
50820            {
50821                 cls : ' x-signature-btn x-signature-'+id,
50822                 scope: editor, // was editor...
50823                 handler: this.reset,
50824                 clickEvent:'mousedown',
50825                 text: this.labels.clear
50826             },
50827             {
50828                  xtype : 'Fill',
50829                  xns: Roo.Toolbar
50830             }, 
50831             {
50832                 cls : '  x-signature-btn x-signature-'+id,
50833                 scope: editor, // was editor...
50834                 handler: this.confirmHandler,
50835                 clickEvent:'mousedown',
50836                 text: this.labels.confirm
50837             }
50838         );
50839     
50840     },
50841     //public
50842     /**
50843      * when user is clicked confirm then show this image.....
50844      * 
50845      * @return {String} Image Data URI
50846      */
50847     getImageDataURI : function(){
50848         var svg = this.svgEl.dom.parentNode.innerHTML;
50849         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50850         return src; 
50851     },
50852     /**
50853      * 
50854      * @return {Boolean} this.isConfirmed
50855      */
50856     getConfirmed : function(){
50857         return this.isConfirmed;
50858     },
50859     /**
50860      * 
50861      * @return {Number} this.width
50862      */
50863     getWidth : function(){
50864         return this.width;
50865     },
50866     /**
50867      * 
50868      * @return {Number} this.height
50869      */
50870     getHeight : function(){
50871         return this.height;
50872     },
50873     // private
50874     getSignature : function(){
50875         return this.signatureTmp;
50876     },
50877     // private
50878     reset : function(){
50879         this.signatureTmp = '';
50880         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50881         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50882         this.isConfirmed = false;
50883         Roo.form.Signature.superclass.reset.call(this);
50884     },
50885     setSignature : function(s){
50886         this.signatureTmp = s;
50887         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50888         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50889         this.setValue(s);
50890         this.isConfirmed = false;
50891         Roo.form.Signature.superclass.reset.call(this);
50892     }, 
50893     test : function(){
50894 //        Roo.log(this.signPanel.dom.contentWindow.up())
50895     },
50896     //private
50897     setConfirmed : function(){
50898         
50899         
50900         
50901 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50902     },
50903     // private
50904     confirmHandler : function(){
50905         if(!this.getSignature()){
50906             return;
50907         }
50908         
50909         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50910         this.setValue(this.getSignature());
50911         this.isConfirmed = true;
50912         
50913         this.fireEvent('confirm', this);
50914     },
50915     // private
50916     // Subclasses should provide the validation implementation by overriding this
50917     validateValue : function(value){
50918         if(this.allowBlank){
50919             return true;
50920         }
50921         
50922         if(this.isConfirmed){
50923             return true;
50924         }
50925         return false;
50926     }
50927 });/*
50928  * Based on:
50929  * Ext JS Library 1.1.1
50930  * Copyright(c) 2006-2007, Ext JS, LLC.
50931  *
50932  * Originally Released Under LGPL - original licence link has changed is not relivant.
50933  *
50934  * Fork - LGPL
50935  * <script type="text/javascript">
50936  */
50937  
50938
50939 /**
50940  * @class Roo.form.ComboBox
50941  * @extends Roo.form.TriggerField
50942  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
50943  * @constructor
50944  * Create a new ComboBox.
50945  * @param {Object} config Configuration options
50946  */
50947 Roo.form.Select = function(config){
50948     Roo.form.Select.superclass.constructor.call(this, config);
50949      
50950 };
50951
50952 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
50953     /**
50954      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
50955      */
50956     /**
50957      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
50958      * rendering into an Roo.Editor, defaults to false)
50959      */
50960     /**
50961      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
50962      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
50963      */
50964     /**
50965      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
50966      */
50967     /**
50968      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
50969      * the dropdown list (defaults to undefined, with no header element)
50970      */
50971
50972      /**
50973      * @cfg {String/Roo.Template} tpl The template to use to render the output
50974      */
50975      
50976     // private
50977     defaultAutoCreate : {tag: "select"  },
50978     /**
50979      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
50980      */
50981     listWidth: undefined,
50982     /**
50983      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
50984      * mode = 'remote' or 'text' if mode = 'local')
50985      */
50986     displayField: undefined,
50987     /**
50988      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
50989      * mode = 'remote' or 'value' if mode = 'local'). 
50990      * Note: use of a valueField requires the user make a selection
50991      * in order for a value to be mapped.
50992      */
50993     valueField: undefined,
50994     
50995     
50996     /**
50997      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
50998      * field's data value (defaults to the underlying DOM element's name)
50999      */
51000     hiddenName: undefined,
51001     /**
51002      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51003      */
51004     listClass: '',
51005     /**
51006      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51007      */
51008     selectedClass: 'x-combo-selected',
51009     /**
51010      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51011      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51012      * which displays a downward arrow icon).
51013      */
51014     triggerClass : 'x-form-arrow-trigger',
51015     /**
51016      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51017      */
51018     shadow:'sides',
51019     /**
51020      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51021      * anchor positions (defaults to 'tl-bl')
51022      */
51023     listAlign: 'tl-bl?',
51024     /**
51025      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51026      */
51027     maxHeight: 300,
51028     /**
51029      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51030      * query specified by the allQuery config option (defaults to 'query')
51031      */
51032     triggerAction: 'query',
51033     /**
51034      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51035      * (defaults to 4, does not apply if editable = false)
51036      */
51037     minChars : 4,
51038     /**
51039      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51040      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51041      */
51042     typeAhead: false,
51043     /**
51044      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51045      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51046      */
51047     queryDelay: 500,
51048     /**
51049      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51050      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51051      */
51052     pageSize: 0,
51053     /**
51054      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51055      * when editable = true (defaults to false)
51056      */
51057     selectOnFocus:false,
51058     /**
51059      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51060      */
51061     queryParam: 'query',
51062     /**
51063      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51064      * when mode = 'remote' (defaults to 'Loading...')
51065      */
51066     loadingText: 'Loading...',
51067     /**
51068      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51069      */
51070     resizable: false,
51071     /**
51072      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51073      */
51074     handleHeight : 8,
51075     /**
51076      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51077      * traditional select (defaults to true)
51078      */
51079     editable: true,
51080     /**
51081      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51082      */
51083     allQuery: '',
51084     /**
51085      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51086      */
51087     mode: 'remote',
51088     /**
51089      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51090      * listWidth has a higher value)
51091      */
51092     minListWidth : 70,
51093     /**
51094      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51095      * allow the user to set arbitrary text into the field (defaults to false)
51096      */
51097     forceSelection:false,
51098     /**
51099      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51100      * if typeAhead = true (defaults to 250)
51101      */
51102     typeAheadDelay : 250,
51103     /**
51104      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51105      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51106      */
51107     valueNotFoundText : undefined,
51108     
51109     /**
51110      * @cfg {String} defaultValue The value displayed after loading the store.
51111      */
51112     defaultValue: '',
51113     
51114     /**
51115      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51116      */
51117     blockFocus : false,
51118     
51119     /**
51120      * @cfg {Boolean} disableClear Disable showing of clear button.
51121      */
51122     disableClear : false,
51123     /**
51124      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51125      */
51126     alwaysQuery : false,
51127     
51128     //private
51129     addicon : false,
51130     editicon: false,
51131     
51132     // element that contains real text value.. (when hidden is used..)
51133      
51134     // private
51135     onRender : function(ct, position){
51136         Roo.form.Field.prototype.onRender.call(this, ct, position);
51137         
51138         if(this.store){
51139             this.store.on('beforeload', this.onBeforeLoad, this);
51140             this.store.on('load', this.onLoad, this);
51141             this.store.on('loadexception', this.onLoadException, this);
51142             this.store.load({});
51143         }
51144         
51145         
51146         
51147     },
51148
51149     // private
51150     initEvents : function(){
51151         //Roo.form.ComboBox.superclass.initEvents.call(this);
51152  
51153     },
51154
51155     onDestroy : function(){
51156        
51157         if(this.store){
51158             this.store.un('beforeload', this.onBeforeLoad, this);
51159             this.store.un('load', this.onLoad, this);
51160             this.store.un('loadexception', this.onLoadException, this);
51161         }
51162         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51163     },
51164
51165     // private
51166     fireKey : function(e){
51167         if(e.isNavKeyPress() && !this.list.isVisible()){
51168             this.fireEvent("specialkey", this, e);
51169         }
51170     },
51171
51172     // private
51173     onResize: function(w, h){
51174         
51175         return; 
51176     
51177         
51178     },
51179
51180     /**
51181      * Allow or prevent the user from directly editing the field text.  If false is passed,
51182      * the user will only be able to select from the items defined in the dropdown list.  This method
51183      * is the runtime equivalent of setting the 'editable' config option at config time.
51184      * @param {Boolean} value True to allow the user to directly edit the field text
51185      */
51186     setEditable : function(value){
51187          
51188     },
51189
51190     // private
51191     onBeforeLoad : function(){
51192         
51193         Roo.log("Select before load");
51194         return;
51195     
51196         this.innerList.update(this.loadingText ?
51197                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51198         //this.restrictHeight();
51199         this.selectedIndex = -1;
51200     },
51201
51202     // private
51203     onLoad : function(){
51204
51205     
51206         var dom = this.el.dom;
51207         dom.innerHTML = '';
51208          var od = dom.ownerDocument;
51209          
51210         if (this.emptyText) {
51211             var op = od.createElement('option');
51212             op.setAttribute('value', '');
51213             op.innerHTML = String.format('{0}', this.emptyText);
51214             dom.appendChild(op);
51215         }
51216         if(this.store.getCount() > 0){
51217            
51218             var vf = this.valueField;
51219             var df = this.displayField;
51220             this.store.data.each(function(r) {
51221                 // which colmsn to use... testing - cdoe / title..
51222                 var op = od.createElement('option');
51223                 op.setAttribute('value', r.data[vf]);
51224                 op.innerHTML = String.format('{0}', r.data[df]);
51225                 dom.appendChild(op);
51226             });
51227             if (typeof(this.defaultValue != 'undefined')) {
51228                 this.setValue(this.defaultValue);
51229             }
51230             
51231              
51232         }else{
51233             //this.onEmptyResults();
51234         }
51235         //this.el.focus();
51236     },
51237     // private
51238     onLoadException : function()
51239     {
51240         dom.innerHTML = '';
51241             
51242         Roo.log("Select on load exception");
51243         return;
51244     
51245         this.collapse();
51246         Roo.log(this.store.reader.jsonData);
51247         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51248             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51249         }
51250         
51251         
51252     },
51253     // private
51254     onTypeAhead : function(){
51255          
51256     },
51257
51258     // private
51259     onSelect : function(record, index){
51260         Roo.log('on select?');
51261         return;
51262         if(this.fireEvent('beforeselect', this, record, index) !== false){
51263             this.setFromData(index > -1 ? record.data : false);
51264             this.collapse();
51265             this.fireEvent('select', this, record, index);
51266         }
51267     },
51268
51269     /**
51270      * Returns the currently selected field value or empty string if no value is set.
51271      * @return {String} value The selected value
51272      */
51273     getValue : function(){
51274         var dom = this.el.dom;
51275         this.value = dom.options[dom.selectedIndex].value;
51276         return this.value;
51277         
51278     },
51279
51280     /**
51281      * Clears any text/value currently set in the field
51282      */
51283     clearValue : function(){
51284         this.value = '';
51285         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51286         
51287     },
51288
51289     /**
51290      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51291      * will be displayed in the field.  If the value does not match the data value of an existing item,
51292      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51293      * Otherwise the field will be blank (although the value will still be set).
51294      * @param {String} value The value to match
51295      */
51296     setValue : function(v){
51297         var d = this.el.dom;
51298         for (var i =0; i < d.options.length;i++) {
51299             if (v == d.options[i].value) {
51300                 d.selectedIndex = i;
51301                 this.value = v;
51302                 return;
51303             }
51304         }
51305         this.clearValue();
51306     },
51307     /**
51308      * @property {Object} the last set data for the element
51309      */
51310     
51311     lastData : false,
51312     /**
51313      * Sets the value of the field based on a object which is related to the record format for the store.
51314      * @param {Object} value the value to set as. or false on reset?
51315      */
51316     setFromData : function(o){
51317         Roo.log('setfrom data?');
51318          
51319         
51320         
51321     },
51322     // private
51323     reset : function(){
51324         this.clearValue();
51325     },
51326     // private
51327     findRecord : function(prop, value){
51328         
51329         return false;
51330     
51331         var record;
51332         if(this.store.getCount() > 0){
51333             this.store.each(function(r){
51334                 if(r.data[prop] == value){
51335                     record = r;
51336                     return false;
51337                 }
51338                 return true;
51339             });
51340         }
51341         return record;
51342     },
51343     
51344     getName: function()
51345     {
51346         // returns hidden if it's set..
51347         if (!this.rendered) {return ''};
51348         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51349         
51350     },
51351      
51352
51353     
51354
51355     // private
51356     onEmptyResults : function(){
51357         Roo.log('empty results');
51358         //this.collapse();
51359     },
51360
51361     /**
51362      * Returns true if the dropdown list is expanded, else false.
51363      */
51364     isExpanded : function(){
51365         return false;
51366     },
51367
51368     /**
51369      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51370      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51371      * @param {String} value The data value of the item to select
51372      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51373      * selected item if it is not currently in view (defaults to true)
51374      * @return {Boolean} True if the value matched an item in the list, else false
51375      */
51376     selectByValue : function(v, scrollIntoView){
51377         Roo.log('select By Value');
51378         return false;
51379     
51380         if(v !== undefined && v !== null){
51381             var r = this.findRecord(this.valueField || this.displayField, v);
51382             if(r){
51383                 this.select(this.store.indexOf(r), scrollIntoView);
51384                 return true;
51385             }
51386         }
51387         return false;
51388     },
51389
51390     /**
51391      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51392      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51393      * @param {Number} index The zero-based index of the list item to select
51394      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51395      * selected item if it is not currently in view (defaults to true)
51396      */
51397     select : function(index, scrollIntoView){
51398         Roo.log('select ');
51399         return  ;
51400         
51401         this.selectedIndex = index;
51402         this.view.select(index);
51403         if(scrollIntoView !== false){
51404             var el = this.view.getNode(index);
51405             if(el){
51406                 this.innerList.scrollChildIntoView(el, false);
51407             }
51408         }
51409     },
51410
51411       
51412
51413     // private
51414     validateBlur : function(){
51415         
51416         return;
51417         
51418     },
51419
51420     // private
51421     initQuery : function(){
51422         this.doQuery(this.getRawValue());
51423     },
51424
51425     // private
51426     doForce : function(){
51427         if(this.el.dom.value.length > 0){
51428             this.el.dom.value =
51429                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51430              
51431         }
51432     },
51433
51434     /**
51435      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51436      * query allowing the query action to be canceled if needed.
51437      * @param {String} query The SQL query to execute
51438      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51439      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51440      * saved in the current store (defaults to false)
51441      */
51442     doQuery : function(q, forceAll){
51443         
51444         Roo.log('doQuery?');
51445         if(q === undefined || q === null){
51446             q = '';
51447         }
51448         var qe = {
51449             query: q,
51450             forceAll: forceAll,
51451             combo: this,
51452             cancel:false
51453         };
51454         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51455             return false;
51456         }
51457         q = qe.query;
51458         forceAll = qe.forceAll;
51459         if(forceAll === true || (q.length >= this.minChars)){
51460             if(this.lastQuery != q || this.alwaysQuery){
51461                 this.lastQuery = q;
51462                 if(this.mode == 'local'){
51463                     this.selectedIndex = -1;
51464                     if(forceAll){
51465                         this.store.clearFilter();
51466                     }else{
51467                         this.store.filter(this.displayField, q);
51468                     }
51469                     this.onLoad();
51470                 }else{
51471                     this.store.baseParams[this.queryParam] = q;
51472                     this.store.load({
51473                         params: this.getParams(q)
51474                     });
51475                     this.expand();
51476                 }
51477             }else{
51478                 this.selectedIndex = -1;
51479                 this.onLoad();   
51480             }
51481         }
51482     },
51483
51484     // private
51485     getParams : function(q){
51486         var p = {};
51487         //p[this.queryParam] = q;
51488         if(this.pageSize){
51489             p.start = 0;
51490             p.limit = this.pageSize;
51491         }
51492         return p;
51493     },
51494
51495     /**
51496      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51497      */
51498     collapse : function(){
51499         
51500     },
51501
51502     // private
51503     collapseIf : function(e){
51504         
51505     },
51506
51507     /**
51508      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51509      */
51510     expand : function(){
51511         
51512     } ,
51513
51514     // private
51515      
51516
51517     /** 
51518     * @cfg {Boolean} grow 
51519     * @hide 
51520     */
51521     /** 
51522     * @cfg {Number} growMin 
51523     * @hide 
51524     */
51525     /** 
51526     * @cfg {Number} growMax 
51527     * @hide 
51528     */
51529     /**
51530      * @hide
51531      * @method autoSize
51532      */
51533     
51534     setWidth : function()
51535     {
51536         
51537     },
51538     getResizeEl : function(){
51539         return this.el;
51540     }
51541 });//<script type="text/javasscript">
51542  
51543
51544 /**
51545  * @class Roo.DDView
51546  * A DnD enabled version of Roo.View.
51547  * @param {Element/String} container The Element in which to create the View.
51548  * @param {String} tpl The template string used to create the markup for each element of the View
51549  * @param {Object} config The configuration properties. These include all the config options of
51550  * {@link Roo.View} plus some specific to this class.<br>
51551  * <p>
51552  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51553  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51554  * <p>
51555  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51556 .x-view-drag-insert-above {
51557         border-top:1px dotted #3366cc;
51558 }
51559 .x-view-drag-insert-below {
51560         border-bottom:1px dotted #3366cc;
51561 }
51562 </code></pre>
51563  * 
51564  */
51565  
51566 Roo.DDView = function(container, tpl, config) {
51567     Roo.DDView.superclass.constructor.apply(this, arguments);
51568     this.getEl().setStyle("outline", "0px none");
51569     this.getEl().unselectable();
51570     if (this.dragGroup) {
51571         this.setDraggable(this.dragGroup.split(","));
51572     }
51573     if (this.dropGroup) {
51574         this.setDroppable(this.dropGroup.split(","));
51575     }
51576     if (this.deletable) {
51577         this.setDeletable();
51578     }
51579     this.isDirtyFlag = false;
51580         this.addEvents({
51581                 "drop" : true
51582         });
51583 };
51584
51585 Roo.extend(Roo.DDView, Roo.View, {
51586 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51587 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51588 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51589 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51590
51591         isFormField: true,
51592
51593         reset: Roo.emptyFn,
51594         
51595         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51596
51597         validate: function() {
51598                 return true;
51599         },
51600         
51601         destroy: function() {
51602                 this.purgeListeners();
51603                 this.getEl.removeAllListeners();
51604                 this.getEl().remove();
51605                 if (this.dragZone) {
51606                         if (this.dragZone.destroy) {
51607                                 this.dragZone.destroy();
51608                         }
51609                 }
51610                 if (this.dropZone) {
51611                         if (this.dropZone.destroy) {
51612                                 this.dropZone.destroy();
51613                         }
51614                 }
51615         },
51616
51617 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51618         getName: function() {
51619                 return this.name;
51620         },
51621
51622 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51623         setValue: function(v) {
51624                 if (!this.store) {
51625                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51626                 }
51627                 var data = {};
51628                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51629                 this.store.proxy = new Roo.data.MemoryProxy(data);
51630                 this.store.load();
51631         },
51632
51633 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51634         getValue: function() {
51635                 var result = '(';
51636                 this.store.each(function(rec) {
51637                         result += rec.id + ',';
51638                 });
51639                 return result.substr(0, result.length - 1) + ')';
51640         },
51641         
51642         getIds: function() {
51643                 var i = 0, result = new Array(this.store.getCount());
51644                 this.store.each(function(rec) {
51645                         result[i++] = rec.id;
51646                 });
51647                 return result;
51648         },
51649         
51650         isDirty: function() {
51651                 return this.isDirtyFlag;
51652         },
51653
51654 /**
51655  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51656  *      whole Element becomes the target, and this causes the drop gesture to append.
51657  */
51658     getTargetFromEvent : function(e) {
51659                 var target = e.getTarget();
51660                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51661                 target = target.parentNode;
51662                 }
51663                 if (!target) {
51664                         target = this.el.dom.lastChild || this.el.dom;
51665                 }
51666                 return target;
51667     },
51668
51669 /**
51670  *      Create the drag data which consists of an object which has the property "ddel" as
51671  *      the drag proxy element. 
51672  */
51673     getDragData : function(e) {
51674         var target = this.findItemFromChild(e.getTarget());
51675                 if(target) {
51676                         this.handleSelection(e);
51677                         var selNodes = this.getSelectedNodes();
51678             var dragData = {
51679                 source: this,
51680                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51681                 nodes: selNodes,
51682                 records: []
51683                         };
51684                         var selectedIndices = this.getSelectedIndexes();
51685                         for (var i = 0; i < selectedIndices.length; i++) {
51686                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51687                         }
51688                         if (selNodes.length == 1) {
51689                                 dragData.ddel = target.cloneNode(true); // the div element
51690                         } else {
51691                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51692                                 div.className = 'multi-proxy';
51693                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51694                                         div.appendChild(selNodes[i].cloneNode(true));
51695                                 }
51696                                 dragData.ddel = div;
51697                         }
51698             //console.log(dragData)
51699             //console.log(dragData.ddel.innerHTML)
51700                         return dragData;
51701                 }
51702         //console.log('nodragData')
51703                 return false;
51704     },
51705     
51706 /**     Specify to which ddGroup items in this DDView may be dragged. */
51707     setDraggable: function(ddGroup) {
51708         if (ddGroup instanceof Array) {
51709                 Roo.each(ddGroup, this.setDraggable, this);
51710                 return;
51711         }
51712         if (this.dragZone) {
51713                 this.dragZone.addToGroup(ddGroup);
51714         } else {
51715                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51716                                 containerScroll: true,
51717                                 ddGroup: ddGroup 
51718
51719                         });
51720 //                      Draggability implies selection. DragZone's mousedown selects the element.
51721                         if (!this.multiSelect) { this.singleSelect = true; }
51722
51723 //                      Wire the DragZone's handlers up to methods in *this*
51724                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51725                 }
51726     },
51727
51728 /**     Specify from which ddGroup this DDView accepts drops. */
51729     setDroppable: function(ddGroup) {
51730         if (ddGroup instanceof Array) {
51731                 Roo.each(ddGroup, this.setDroppable, this);
51732                 return;
51733         }
51734         if (this.dropZone) {
51735                 this.dropZone.addToGroup(ddGroup);
51736         } else {
51737                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51738                                 containerScroll: true,
51739                                 ddGroup: ddGroup
51740                         });
51741
51742 //                      Wire the DropZone's handlers up to methods in *this*
51743                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51744                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51745                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51746                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51747                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51748                 }
51749     },
51750
51751 /**     Decide whether to drop above or below a View node. */
51752     getDropPoint : function(e, n, dd){
51753         if (n == this.el.dom) { return "above"; }
51754                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51755                 var c = t + (b - t) / 2;
51756                 var y = Roo.lib.Event.getPageY(e);
51757                 if(y <= c) {
51758                         return "above";
51759                 }else{
51760                         return "below";
51761                 }
51762     },
51763
51764     onNodeEnter : function(n, dd, e, data){
51765                 return false;
51766     },
51767     
51768     onNodeOver : function(n, dd, e, data){
51769                 var pt = this.getDropPoint(e, n, dd);
51770                 // set the insert point style on the target node
51771                 var dragElClass = this.dropNotAllowed;
51772                 if (pt) {
51773                         var targetElClass;
51774                         if (pt == "above"){
51775                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51776                                 targetElClass = "x-view-drag-insert-above";
51777                         } else {
51778                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51779                                 targetElClass = "x-view-drag-insert-below";
51780                         }
51781                         if (this.lastInsertClass != targetElClass){
51782                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51783                                 this.lastInsertClass = targetElClass;
51784                         }
51785                 }
51786                 return dragElClass;
51787         },
51788
51789     onNodeOut : function(n, dd, e, data){
51790                 this.removeDropIndicators(n);
51791     },
51792
51793     onNodeDrop : function(n, dd, e, data){
51794         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51795                 return false;
51796         }
51797         var pt = this.getDropPoint(e, n, dd);
51798                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51799                 if (pt == "below") { insertAt++; }
51800                 for (var i = 0; i < data.records.length; i++) {
51801                         var r = data.records[i];
51802                         var dup = this.store.getById(r.id);
51803                         if (dup && (dd != this.dragZone)) {
51804                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51805                         } else {
51806                                 if (data.copy) {
51807                                         this.store.insert(insertAt++, r.copy());
51808                                 } else {
51809                                         data.source.isDirtyFlag = true;
51810                                         r.store.remove(r);
51811                                         this.store.insert(insertAt++, r);
51812                                 }
51813                                 this.isDirtyFlag = true;
51814                         }
51815                 }
51816                 this.dragZone.cachedTarget = null;
51817                 return true;
51818     },
51819
51820     removeDropIndicators : function(n){
51821                 if(n){
51822                         Roo.fly(n).removeClass([
51823                                 "x-view-drag-insert-above",
51824                                 "x-view-drag-insert-below"]);
51825                         this.lastInsertClass = "_noclass";
51826                 }
51827     },
51828
51829 /**
51830  *      Utility method. Add a delete option to the DDView's context menu.
51831  *      @param {String} imageUrl The URL of the "delete" icon image.
51832  */
51833         setDeletable: function(imageUrl) {
51834                 if (!this.singleSelect && !this.multiSelect) {
51835                         this.singleSelect = true;
51836                 }
51837                 var c = this.getContextMenu();
51838                 this.contextMenu.on("itemclick", function(item) {
51839                         switch (item.id) {
51840                                 case "delete":
51841                                         this.remove(this.getSelectedIndexes());
51842                                         break;
51843                         }
51844                 }, this);
51845                 this.contextMenu.add({
51846                         icon: imageUrl,
51847                         id: "delete",
51848                         text: 'Delete'
51849                 });
51850         },
51851         
51852 /**     Return the context menu for this DDView. */
51853         getContextMenu: function() {
51854                 if (!this.contextMenu) {
51855 //                      Create the View's context menu
51856                         this.contextMenu = new Roo.menu.Menu({
51857                                 id: this.id + "-contextmenu"
51858                         });
51859                         this.el.on("contextmenu", this.showContextMenu, this);
51860                 }
51861                 return this.contextMenu;
51862         },
51863         
51864         disableContextMenu: function() {
51865                 if (this.contextMenu) {
51866                         this.el.un("contextmenu", this.showContextMenu, this);
51867                 }
51868         },
51869
51870         showContextMenu: function(e, item) {
51871         item = this.findItemFromChild(e.getTarget());
51872                 if (item) {
51873                         e.stopEvent();
51874                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51875                         this.contextMenu.showAt(e.getXY());
51876             }
51877     },
51878
51879 /**
51880  *      Remove {@link Roo.data.Record}s at the specified indices.
51881  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51882  */
51883     remove: function(selectedIndices) {
51884                 selectedIndices = [].concat(selectedIndices);
51885                 for (var i = 0; i < selectedIndices.length; i++) {
51886                         var rec = this.store.getAt(selectedIndices[i]);
51887                         this.store.remove(rec);
51888                 }
51889     },
51890
51891 /**
51892  *      Double click fires the event, but also, if this is draggable, and there is only one other
51893  *      related DropZone, it transfers the selected node.
51894  */
51895     onDblClick : function(e){
51896         var item = this.findItemFromChild(e.getTarget());
51897         if(item){
51898             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51899                 return false;
51900             }
51901             if (this.dragGroup) {
51902                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51903                     while (targets.indexOf(this.dropZone) > -1) {
51904                             targets.remove(this.dropZone);
51905                                 }
51906                     if (targets.length == 1) {
51907                                         this.dragZone.cachedTarget = null;
51908                         var el = Roo.get(targets[0].getEl());
51909                         var box = el.getBox(true);
51910                         targets[0].onNodeDrop(el.dom, {
51911                                 target: el.dom,
51912                                 xy: [box.x, box.y + box.height - 1]
51913                         }, null, this.getDragData(e));
51914                     }
51915                 }
51916         }
51917     },
51918     
51919     handleSelection: function(e) {
51920                 this.dragZone.cachedTarget = null;
51921         var item = this.findItemFromChild(e.getTarget());
51922         if (!item) {
51923                 this.clearSelections(true);
51924                 return;
51925         }
51926                 if (item && (this.multiSelect || this.singleSelect)){
51927                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51928                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51929                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51930                                 this.unselect(item);
51931                         } else {
51932                                 this.select(item, this.multiSelect && e.ctrlKey);
51933                                 this.lastSelection = item;
51934                         }
51935                 }
51936     },
51937
51938     onItemClick : function(item, index, e){
51939                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
51940                         return false;
51941                 }
51942                 return true;
51943     },
51944
51945     unselect : function(nodeInfo, suppressEvent){
51946                 var node = this.getNode(nodeInfo);
51947                 if(node && this.isSelected(node)){
51948                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
51949                                 Roo.fly(node).removeClass(this.selectedClass);
51950                                 this.selections.remove(node);
51951                                 if(!suppressEvent){
51952                                         this.fireEvent("selectionchange", this, this.selections);
51953                                 }
51954                         }
51955                 }
51956     }
51957 });
51958 /*
51959  * Based on:
51960  * Ext JS Library 1.1.1
51961  * Copyright(c) 2006-2007, Ext JS, LLC.
51962  *
51963  * Originally Released Under LGPL - original licence link has changed is not relivant.
51964  *
51965  * Fork - LGPL
51966  * <script type="text/javascript">
51967  */
51968  
51969 /**
51970  * @class Roo.LayoutManager
51971  * @extends Roo.util.Observable
51972  * Base class for layout managers.
51973  */
51974 Roo.LayoutManager = function(container, config){
51975     Roo.LayoutManager.superclass.constructor.call(this);
51976     this.el = Roo.get(container);
51977     // ie scrollbar fix
51978     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
51979         document.body.scroll = "no";
51980     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
51981         this.el.position('relative');
51982     }
51983     this.id = this.el.id;
51984     this.el.addClass("x-layout-container");
51985     /** false to disable window resize monitoring @type Boolean */
51986     this.monitorWindowResize = true;
51987     this.regions = {};
51988     this.addEvents({
51989         /**
51990          * @event layout
51991          * Fires when a layout is performed. 
51992          * @param {Roo.LayoutManager} this
51993          */
51994         "layout" : true,
51995         /**
51996          * @event regionresized
51997          * Fires when the user resizes a region. 
51998          * @param {Roo.LayoutRegion} region The resized region
51999          * @param {Number} newSize The new size (width for east/west, height for north/south)
52000          */
52001         "regionresized" : true,
52002         /**
52003          * @event regioncollapsed
52004          * Fires when a region is collapsed. 
52005          * @param {Roo.LayoutRegion} region The collapsed region
52006          */
52007         "regioncollapsed" : true,
52008         /**
52009          * @event regionexpanded
52010          * Fires when a region is expanded.  
52011          * @param {Roo.LayoutRegion} region The expanded region
52012          */
52013         "regionexpanded" : true
52014     });
52015     this.updating = false;
52016     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52017 };
52018
52019 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52020     /**
52021      * Returns true if this layout is currently being updated
52022      * @return {Boolean}
52023      */
52024     isUpdating : function(){
52025         return this.updating; 
52026     },
52027     
52028     /**
52029      * Suspend the LayoutManager from doing auto-layouts while
52030      * making multiple add or remove calls
52031      */
52032     beginUpdate : function(){
52033         this.updating = true;    
52034     },
52035     
52036     /**
52037      * Restore auto-layouts and optionally disable the manager from performing a layout
52038      * @param {Boolean} noLayout true to disable a layout update 
52039      */
52040     endUpdate : function(noLayout){
52041         this.updating = false;
52042         if(!noLayout){
52043             this.layout();
52044         }    
52045     },
52046     
52047     layout: function(){
52048         
52049     },
52050     
52051     onRegionResized : function(region, newSize){
52052         this.fireEvent("regionresized", region, newSize);
52053         this.layout();
52054     },
52055     
52056     onRegionCollapsed : function(region){
52057         this.fireEvent("regioncollapsed", region);
52058     },
52059     
52060     onRegionExpanded : function(region){
52061         this.fireEvent("regionexpanded", region);
52062     },
52063         
52064     /**
52065      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52066      * performs box-model adjustments.
52067      * @return {Object} The size as an object {width: (the width), height: (the height)}
52068      */
52069     getViewSize : function(){
52070         var size;
52071         if(this.el.dom != document.body){
52072             size = this.el.getSize();
52073         }else{
52074             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52075         }
52076         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52077         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52078         return size;
52079     },
52080     
52081     /**
52082      * Returns the Element this layout is bound to.
52083      * @return {Roo.Element}
52084      */
52085     getEl : function(){
52086         return this.el;
52087     },
52088     
52089     /**
52090      * Returns the specified region.
52091      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52092      * @return {Roo.LayoutRegion}
52093      */
52094     getRegion : function(target){
52095         return this.regions[target.toLowerCase()];
52096     },
52097     
52098     onWindowResize : function(){
52099         if(this.monitorWindowResize){
52100             this.layout();
52101         }
52102     }
52103 });/*
52104  * Based on:
52105  * Ext JS Library 1.1.1
52106  * Copyright(c) 2006-2007, Ext JS, LLC.
52107  *
52108  * Originally Released Under LGPL - original licence link has changed is not relivant.
52109  *
52110  * Fork - LGPL
52111  * <script type="text/javascript">
52112  */
52113 /**
52114  * @class Roo.BorderLayout
52115  * @extends Roo.LayoutManager
52116  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52117  * please see: <br><br>
52118  * <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>
52119  * <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>
52120  * Example:
52121  <pre><code>
52122  var layout = new Roo.BorderLayout(document.body, {
52123     north: {
52124         initialSize: 25,
52125         titlebar: false
52126     },
52127     west: {
52128         split:true,
52129         initialSize: 200,
52130         minSize: 175,
52131         maxSize: 400,
52132         titlebar: true,
52133         collapsible: true
52134     },
52135     east: {
52136         split:true,
52137         initialSize: 202,
52138         minSize: 175,
52139         maxSize: 400,
52140         titlebar: true,
52141         collapsible: true
52142     },
52143     south: {
52144         split:true,
52145         initialSize: 100,
52146         minSize: 100,
52147         maxSize: 200,
52148         titlebar: true,
52149         collapsible: true
52150     },
52151     center: {
52152         titlebar: true,
52153         autoScroll:true,
52154         resizeTabs: true,
52155         minTabWidth: 50,
52156         preferredTabWidth: 150
52157     }
52158 });
52159
52160 // shorthand
52161 var CP = Roo.ContentPanel;
52162
52163 layout.beginUpdate();
52164 layout.add("north", new CP("north", "North"));
52165 layout.add("south", new CP("south", {title: "South", closable: true}));
52166 layout.add("west", new CP("west", {title: "West"}));
52167 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52168 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52169 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52170 layout.getRegion("center").showPanel("center1");
52171 layout.endUpdate();
52172 </code></pre>
52173
52174 <b>The container the layout is rendered into can be either the body element or any other element.
52175 If it is not the body element, the container needs to either be an absolute positioned element,
52176 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52177 the container size if it is not the body element.</b>
52178
52179 * @constructor
52180 * Create a new BorderLayout
52181 * @param {String/HTMLElement/Element} container The container this layout is bound to
52182 * @param {Object} config Configuration options
52183  */
52184 Roo.BorderLayout = function(container, config){
52185     config = config || {};
52186     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52187     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52188     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52189         var target = this.factory.validRegions[i];
52190         if(config[target]){
52191             this.addRegion(target, config[target]);
52192         }
52193     }
52194 };
52195
52196 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52197     /**
52198      * Creates and adds a new region if it doesn't already exist.
52199      * @param {String} target The target region key (north, south, east, west or center).
52200      * @param {Object} config The regions config object
52201      * @return {BorderLayoutRegion} The new region
52202      */
52203     addRegion : function(target, config){
52204         if(!this.regions[target]){
52205             var r = this.factory.create(target, this, config);
52206             this.bindRegion(target, r);
52207         }
52208         return this.regions[target];
52209     },
52210
52211     // private (kinda)
52212     bindRegion : function(name, r){
52213         this.regions[name] = r;
52214         r.on("visibilitychange", this.layout, this);
52215         r.on("paneladded", this.layout, this);
52216         r.on("panelremoved", this.layout, this);
52217         r.on("invalidated", this.layout, this);
52218         r.on("resized", this.onRegionResized, this);
52219         r.on("collapsed", this.onRegionCollapsed, this);
52220         r.on("expanded", this.onRegionExpanded, this);
52221     },
52222
52223     /**
52224      * Performs a layout update.
52225      */
52226     layout : function(){
52227         if(this.updating) {
52228             return;
52229         }
52230         var size = this.getViewSize();
52231         var w = size.width;
52232         var h = size.height;
52233         var centerW = w;
52234         var centerH = h;
52235         var centerY = 0;
52236         var centerX = 0;
52237         //var x = 0, y = 0;
52238
52239         var rs = this.regions;
52240         var north = rs["north"];
52241         var south = rs["south"]; 
52242         var west = rs["west"];
52243         var east = rs["east"];
52244         var center = rs["center"];
52245         //if(this.hideOnLayout){ // not supported anymore
52246             //c.el.setStyle("display", "none");
52247         //}
52248         if(north && north.isVisible()){
52249             var b = north.getBox();
52250             var m = north.getMargins();
52251             b.width = w - (m.left+m.right);
52252             b.x = m.left;
52253             b.y = m.top;
52254             centerY = b.height + b.y + m.bottom;
52255             centerH -= centerY;
52256             north.updateBox(this.safeBox(b));
52257         }
52258         if(south && south.isVisible()){
52259             var b = south.getBox();
52260             var m = south.getMargins();
52261             b.width = w - (m.left+m.right);
52262             b.x = m.left;
52263             var totalHeight = (b.height + m.top + m.bottom);
52264             b.y = h - totalHeight + m.top;
52265             centerH -= totalHeight;
52266             south.updateBox(this.safeBox(b));
52267         }
52268         if(west && west.isVisible()){
52269             var b = west.getBox();
52270             var m = west.getMargins();
52271             b.height = centerH - (m.top+m.bottom);
52272             b.x = m.left;
52273             b.y = centerY + m.top;
52274             var totalWidth = (b.width + m.left + m.right);
52275             centerX += totalWidth;
52276             centerW -= totalWidth;
52277             west.updateBox(this.safeBox(b));
52278         }
52279         if(east && east.isVisible()){
52280             var b = east.getBox();
52281             var m = east.getMargins();
52282             b.height = centerH - (m.top+m.bottom);
52283             var totalWidth = (b.width + m.left + m.right);
52284             b.x = w - totalWidth + m.left;
52285             b.y = centerY + m.top;
52286             centerW -= totalWidth;
52287             east.updateBox(this.safeBox(b));
52288         }
52289         if(center){
52290             var m = center.getMargins();
52291             var centerBox = {
52292                 x: centerX + m.left,
52293                 y: centerY + m.top,
52294                 width: centerW - (m.left+m.right),
52295                 height: centerH - (m.top+m.bottom)
52296             };
52297             //if(this.hideOnLayout){
52298                 //center.el.setStyle("display", "block");
52299             //}
52300             center.updateBox(this.safeBox(centerBox));
52301         }
52302         this.el.repaint();
52303         this.fireEvent("layout", this);
52304     },
52305
52306     // private
52307     safeBox : function(box){
52308         box.width = Math.max(0, box.width);
52309         box.height = Math.max(0, box.height);
52310         return box;
52311     },
52312
52313     /**
52314      * Adds a ContentPanel (or subclass) to this layout.
52315      * @param {String} target The target region key (north, south, east, west or center).
52316      * @param {Roo.ContentPanel} panel The panel to add
52317      * @return {Roo.ContentPanel} The added panel
52318      */
52319     add : function(target, panel){
52320          
52321         target = target.toLowerCase();
52322         return this.regions[target].add(panel);
52323     },
52324
52325     /**
52326      * Remove a ContentPanel (or subclass) to this layout.
52327      * @param {String} target The target region key (north, south, east, west or center).
52328      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52329      * @return {Roo.ContentPanel} The removed panel
52330      */
52331     remove : function(target, panel){
52332         target = target.toLowerCase();
52333         return this.regions[target].remove(panel);
52334     },
52335
52336     /**
52337      * Searches all regions for a panel with the specified id
52338      * @param {String} panelId
52339      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52340      */
52341     findPanel : function(panelId){
52342         var rs = this.regions;
52343         for(var target in rs){
52344             if(typeof rs[target] != "function"){
52345                 var p = rs[target].getPanel(panelId);
52346                 if(p){
52347                     return p;
52348                 }
52349             }
52350         }
52351         return null;
52352     },
52353
52354     /**
52355      * Searches all regions for a panel with the specified id and activates (shows) it.
52356      * @param {String/ContentPanel} panelId The panels id or the panel itself
52357      * @return {Roo.ContentPanel} The shown panel or null
52358      */
52359     showPanel : function(panelId) {
52360       var rs = this.regions;
52361       for(var target in rs){
52362          var r = rs[target];
52363          if(typeof r != "function"){
52364             if(r.hasPanel(panelId)){
52365                return r.showPanel(panelId);
52366             }
52367          }
52368       }
52369       return null;
52370    },
52371
52372    /**
52373      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52374      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52375      */
52376     restoreState : function(provider){
52377         if(!provider){
52378             provider = Roo.state.Manager;
52379         }
52380         var sm = new Roo.LayoutStateManager();
52381         sm.init(this, provider);
52382     },
52383
52384     /**
52385      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52386      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52387      * a valid ContentPanel config object.  Example:
52388      * <pre><code>
52389 // Create the main layout
52390 var layout = new Roo.BorderLayout('main-ct', {
52391     west: {
52392         split:true,
52393         minSize: 175,
52394         titlebar: true
52395     },
52396     center: {
52397         title:'Components'
52398     }
52399 }, 'main-ct');
52400
52401 // Create and add multiple ContentPanels at once via configs
52402 layout.batchAdd({
52403    west: {
52404        id: 'source-files',
52405        autoCreate:true,
52406        title:'Ext Source Files',
52407        autoScroll:true,
52408        fitToFrame:true
52409    },
52410    center : {
52411        el: cview,
52412        autoScroll:true,
52413        fitToFrame:true,
52414        toolbar: tb,
52415        resizeEl:'cbody'
52416    }
52417 });
52418 </code></pre>
52419      * @param {Object} regions An object containing ContentPanel configs by region name
52420      */
52421     batchAdd : function(regions){
52422         this.beginUpdate();
52423         for(var rname in regions){
52424             var lr = this.regions[rname];
52425             if(lr){
52426                 this.addTypedPanels(lr, regions[rname]);
52427             }
52428         }
52429         this.endUpdate();
52430     },
52431
52432     // private
52433     addTypedPanels : function(lr, ps){
52434         if(typeof ps == 'string'){
52435             lr.add(new Roo.ContentPanel(ps));
52436         }
52437         else if(ps instanceof Array){
52438             for(var i =0, len = ps.length; i < len; i++){
52439                 this.addTypedPanels(lr, ps[i]);
52440             }
52441         }
52442         else if(!ps.events){ // raw config?
52443             var el = ps.el;
52444             delete ps.el; // prevent conflict
52445             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52446         }
52447         else {  // panel object assumed!
52448             lr.add(ps);
52449         }
52450     },
52451     /**
52452      * Adds a xtype elements to the layout.
52453      * <pre><code>
52454
52455 layout.addxtype({
52456        xtype : 'ContentPanel',
52457        region: 'west',
52458        items: [ .... ]
52459    }
52460 );
52461
52462 layout.addxtype({
52463         xtype : 'NestedLayoutPanel',
52464         region: 'west',
52465         layout: {
52466            center: { },
52467            west: { }   
52468         },
52469         items : [ ... list of content panels or nested layout panels.. ]
52470    }
52471 );
52472 </code></pre>
52473      * @param {Object} cfg Xtype definition of item to add.
52474      */
52475     addxtype : function(cfg)
52476     {
52477         // basically accepts a pannel...
52478         // can accept a layout region..!?!?
52479         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52480         
52481         if (!cfg.xtype.match(/Panel$/)) {
52482             return false;
52483         }
52484         var ret = false;
52485         
52486         if (typeof(cfg.region) == 'undefined') {
52487             Roo.log("Failed to add Panel, region was not set");
52488             Roo.log(cfg);
52489             return false;
52490         }
52491         var region = cfg.region;
52492         delete cfg.region;
52493         
52494           
52495         var xitems = [];
52496         if (cfg.items) {
52497             xitems = cfg.items;
52498             delete cfg.items;
52499         }
52500         var nb = false;
52501         
52502         switch(cfg.xtype) 
52503         {
52504             case 'ContentPanel':  // ContentPanel (el, cfg)
52505             case 'ScrollPanel':  // ContentPanel (el, cfg)
52506             case 'ViewPanel': 
52507                 if(cfg.autoCreate) {
52508                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52509                 } else {
52510                     var el = this.el.createChild();
52511                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52512                 }
52513                 
52514                 this.add(region, ret);
52515                 break;
52516             
52517             
52518             case 'TreePanel': // our new panel!
52519                 cfg.el = this.el.createChild();
52520                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52521                 this.add(region, ret);
52522                 break;
52523             
52524             case 'NestedLayoutPanel': 
52525                 // create a new Layout (which is  a Border Layout...
52526                 var el = this.el.createChild();
52527                 var clayout = cfg.layout;
52528                 delete cfg.layout;
52529                 clayout.items   = clayout.items  || [];
52530                 // replace this exitems with the clayout ones..
52531                 xitems = clayout.items;
52532                  
52533                 
52534                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52535                     cfg.background = false;
52536                 }
52537                 var layout = new Roo.BorderLayout(el, clayout);
52538                 
52539                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52540                 //console.log('adding nested layout panel '  + cfg.toSource());
52541                 this.add(region, ret);
52542                 nb = {}; /// find first...
52543                 break;
52544                 
52545             case 'GridPanel': 
52546             
52547                 // needs grid and region
52548                 
52549                 //var el = this.getRegion(region).el.createChild();
52550                 var el = this.el.createChild();
52551                 // create the grid first...
52552                 
52553                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52554                 delete cfg.grid;
52555                 if (region == 'center' && this.active ) {
52556                     cfg.background = false;
52557                 }
52558                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52559                 
52560                 this.add(region, ret);
52561                 if (cfg.background) {
52562                     ret.on('activate', function(gp) {
52563                         if (!gp.grid.rendered) {
52564                             gp.grid.render();
52565                         }
52566                     });
52567                 } else {
52568                     grid.render();
52569                 }
52570                 break;
52571            
52572            
52573            
52574                 
52575                 
52576                 
52577             default:
52578                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52579                     
52580                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52581                     this.add(region, ret);
52582                 } else {
52583                 
52584                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52585                     return null;
52586                 }
52587                 
52588              // GridPanel (grid, cfg)
52589             
52590         }
52591         this.beginUpdate();
52592         // add children..
52593         var region = '';
52594         var abn = {};
52595         Roo.each(xitems, function(i)  {
52596             region = nb && i.region ? i.region : false;
52597             
52598             var add = ret.addxtype(i);
52599            
52600             if (region) {
52601                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52602                 if (!i.background) {
52603                     abn[region] = nb[region] ;
52604                 }
52605             }
52606             
52607         });
52608         this.endUpdate();
52609
52610         // make the last non-background panel active..
52611         //if (nb) { Roo.log(abn); }
52612         if (nb) {
52613             
52614             for(var r in abn) {
52615                 region = this.getRegion(r);
52616                 if (region) {
52617                     // tried using nb[r], but it does not work..
52618                      
52619                     region.showPanel(abn[r]);
52620                    
52621                 }
52622             }
52623         }
52624         return ret;
52625         
52626     }
52627 });
52628
52629 /**
52630  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52631  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52632  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52633  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52634  * <pre><code>
52635 // shorthand
52636 var CP = Roo.ContentPanel;
52637
52638 var layout = Roo.BorderLayout.create({
52639     north: {
52640         initialSize: 25,
52641         titlebar: false,
52642         panels: [new CP("north", "North")]
52643     },
52644     west: {
52645         split:true,
52646         initialSize: 200,
52647         minSize: 175,
52648         maxSize: 400,
52649         titlebar: true,
52650         collapsible: true,
52651         panels: [new CP("west", {title: "West"})]
52652     },
52653     east: {
52654         split:true,
52655         initialSize: 202,
52656         minSize: 175,
52657         maxSize: 400,
52658         titlebar: true,
52659         collapsible: true,
52660         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52661     },
52662     south: {
52663         split:true,
52664         initialSize: 100,
52665         minSize: 100,
52666         maxSize: 200,
52667         titlebar: true,
52668         collapsible: true,
52669         panels: [new CP("south", {title: "South", closable: true})]
52670     },
52671     center: {
52672         titlebar: true,
52673         autoScroll:true,
52674         resizeTabs: true,
52675         minTabWidth: 50,
52676         preferredTabWidth: 150,
52677         panels: [
52678             new CP("center1", {title: "Close Me", closable: true}),
52679             new CP("center2", {title: "Center Panel", closable: false})
52680         ]
52681     }
52682 }, document.body);
52683
52684 layout.getRegion("center").showPanel("center1");
52685 </code></pre>
52686  * @param config
52687  * @param targetEl
52688  */
52689 Roo.BorderLayout.create = function(config, targetEl){
52690     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52691     layout.beginUpdate();
52692     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52693     for(var j = 0, jlen = regions.length; j < jlen; j++){
52694         var lr = regions[j];
52695         if(layout.regions[lr] && config[lr].panels){
52696             var r = layout.regions[lr];
52697             var ps = config[lr].panels;
52698             layout.addTypedPanels(r, ps);
52699         }
52700     }
52701     layout.endUpdate();
52702     return layout;
52703 };
52704
52705 // private
52706 Roo.BorderLayout.RegionFactory = {
52707     // private
52708     validRegions : ["north","south","east","west","center"],
52709
52710     // private
52711     create : function(target, mgr, config){
52712         target = target.toLowerCase();
52713         if(config.lightweight || config.basic){
52714             return new Roo.BasicLayoutRegion(mgr, config, target);
52715         }
52716         switch(target){
52717             case "north":
52718                 return new Roo.NorthLayoutRegion(mgr, config);
52719             case "south":
52720                 return new Roo.SouthLayoutRegion(mgr, config);
52721             case "east":
52722                 return new Roo.EastLayoutRegion(mgr, config);
52723             case "west":
52724                 return new Roo.WestLayoutRegion(mgr, config);
52725             case "center":
52726                 return new Roo.CenterLayoutRegion(mgr, config);
52727         }
52728         throw 'Layout region "'+target+'" not supported.';
52729     }
52730 };/*
52731  * Based on:
52732  * Ext JS Library 1.1.1
52733  * Copyright(c) 2006-2007, Ext JS, LLC.
52734  *
52735  * Originally Released Under LGPL - original licence link has changed is not relivant.
52736  *
52737  * Fork - LGPL
52738  * <script type="text/javascript">
52739  */
52740  
52741 /**
52742  * @class Roo.BasicLayoutRegion
52743  * @extends Roo.util.Observable
52744  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52745  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52746  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52747  */
52748 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52749     this.mgr = mgr;
52750     this.position  = pos;
52751     this.events = {
52752         /**
52753          * @scope Roo.BasicLayoutRegion
52754          */
52755         
52756         /**
52757          * @event beforeremove
52758          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52759          * @param {Roo.LayoutRegion} this
52760          * @param {Roo.ContentPanel} panel The panel
52761          * @param {Object} e The cancel event object
52762          */
52763         "beforeremove" : true,
52764         /**
52765          * @event invalidated
52766          * Fires when the layout for this region is changed.
52767          * @param {Roo.LayoutRegion} this
52768          */
52769         "invalidated" : true,
52770         /**
52771          * @event visibilitychange
52772          * Fires when this region is shown or hidden 
52773          * @param {Roo.LayoutRegion} this
52774          * @param {Boolean} visibility true or false
52775          */
52776         "visibilitychange" : true,
52777         /**
52778          * @event paneladded
52779          * Fires when a panel is added. 
52780          * @param {Roo.LayoutRegion} this
52781          * @param {Roo.ContentPanel} panel The panel
52782          */
52783         "paneladded" : true,
52784         /**
52785          * @event panelremoved
52786          * Fires when a panel is removed. 
52787          * @param {Roo.LayoutRegion} this
52788          * @param {Roo.ContentPanel} panel The panel
52789          */
52790         "panelremoved" : true,
52791         /**
52792          * @event beforecollapse
52793          * Fires when this region before collapse.
52794          * @param {Roo.LayoutRegion} this
52795          */
52796         "beforecollapse" : true,
52797         /**
52798          * @event collapsed
52799          * Fires when this region is collapsed.
52800          * @param {Roo.LayoutRegion} this
52801          */
52802         "collapsed" : true,
52803         /**
52804          * @event expanded
52805          * Fires when this region is expanded.
52806          * @param {Roo.LayoutRegion} this
52807          */
52808         "expanded" : true,
52809         /**
52810          * @event slideshow
52811          * Fires when this region is slid into view.
52812          * @param {Roo.LayoutRegion} this
52813          */
52814         "slideshow" : true,
52815         /**
52816          * @event slidehide
52817          * Fires when this region slides out of view. 
52818          * @param {Roo.LayoutRegion} this
52819          */
52820         "slidehide" : true,
52821         /**
52822          * @event panelactivated
52823          * Fires when a panel is activated. 
52824          * @param {Roo.LayoutRegion} this
52825          * @param {Roo.ContentPanel} panel The activated panel
52826          */
52827         "panelactivated" : true,
52828         /**
52829          * @event resized
52830          * Fires when the user resizes this region. 
52831          * @param {Roo.LayoutRegion} this
52832          * @param {Number} newSize The new size (width for east/west, height for north/south)
52833          */
52834         "resized" : true
52835     };
52836     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52837     this.panels = new Roo.util.MixedCollection();
52838     this.panels.getKey = this.getPanelId.createDelegate(this);
52839     this.box = null;
52840     this.activePanel = null;
52841     // ensure listeners are added...
52842     
52843     if (config.listeners || config.events) {
52844         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52845             listeners : config.listeners || {},
52846             events : config.events || {}
52847         });
52848     }
52849     
52850     if(skipConfig !== true){
52851         this.applyConfig(config);
52852     }
52853 };
52854
52855 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52856     getPanelId : function(p){
52857         return p.getId();
52858     },
52859     
52860     applyConfig : function(config){
52861         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52862         this.config = config;
52863         
52864     },
52865     
52866     /**
52867      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52868      * the width, for horizontal (north, south) the height.
52869      * @param {Number} newSize The new width or height
52870      */
52871     resizeTo : function(newSize){
52872         var el = this.el ? this.el :
52873                  (this.activePanel ? this.activePanel.getEl() : null);
52874         if(el){
52875             switch(this.position){
52876                 case "east":
52877                 case "west":
52878                     el.setWidth(newSize);
52879                     this.fireEvent("resized", this, newSize);
52880                 break;
52881                 case "north":
52882                 case "south":
52883                     el.setHeight(newSize);
52884                     this.fireEvent("resized", this, newSize);
52885                 break;                
52886             }
52887         }
52888     },
52889     
52890     getBox : function(){
52891         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52892     },
52893     
52894     getMargins : function(){
52895         return this.margins;
52896     },
52897     
52898     updateBox : function(box){
52899         this.box = box;
52900         var el = this.activePanel.getEl();
52901         el.dom.style.left = box.x + "px";
52902         el.dom.style.top = box.y + "px";
52903         this.activePanel.setSize(box.width, box.height);
52904     },
52905     
52906     /**
52907      * Returns the container element for this region.
52908      * @return {Roo.Element}
52909      */
52910     getEl : function(){
52911         return this.activePanel;
52912     },
52913     
52914     /**
52915      * Returns true if this region is currently visible.
52916      * @return {Boolean}
52917      */
52918     isVisible : function(){
52919         return this.activePanel ? true : false;
52920     },
52921     
52922     setActivePanel : function(panel){
52923         panel = this.getPanel(panel);
52924         if(this.activePanel && this.activePanel != panel){
52925             this.activePanel.setActiveState(false);
52926             this.activePanel.getEl().setLeftTop(-10000,-10000);
52927         }
52928         this.activePanel = panel;
52929         panel.setActiveState(true);
52930         if(this.box){
52931             panel.setSize(this.box.width, this.box.height);
52932         }
52933         this.fireEvent("panelactivated", this, panel);
52934         this.fireEvent("invalidated");
52935     },
52936     
52937     /**
52938      * Show the specified panel.
52939      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
52940      * @return {Roo.ContentPanel} The shown panel or null
52941      */
52942     showPanel : function(panel){
52943         if(panel = this.getPanel(panel)){
52944             this.setActivePanel(panel);
52945         }
52946         return panel;
52947     },
52948     
52949     /**
52950      * Get the active panel for this region.
52951      * @return {Roo.ContentPanel} The active panel or null
52952      */
52953     getActivePanel : function(){
52954         return this.activePanel;
52955     },
52956     
52957     /**
52958      * Add the passed ContentPanel(s)
52959      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
52960      * @return {Roo.ContentPanel} The panel added (if only one was added)
52961      */
52962     add : function(panel){
52963         if(arguments.length > 1){
52964             for(var i = 0, len = arguments.length; i < len; i++) {
52965                 this.add(arguments[i]);
52966             }
52967             return null;
52968         }
52969         if(this.hasPanel(panel)){
52970             this.showPanel(panel);
52971             return panel;
52972         }
52973         var el = panel.getEl();
52974         if(el.dom.parentNode != this.mgr.el.dom){
52975             this.mgr.el.dom.appendChild(el.dom);
52976         }
52977         if(panel.setRegion){
52978             panel.setRegion(this);
52979         }
52980         this.panels.add(panel);
52981         el.setStyle("position", "absolute");
52982         if(!panel.background){
52983             this.setActivePanel(panel);
52984             if(this.config.initialSize && this.panels.getCount()==1){
52985                 this.resizeTo(this.config.initialSize);
52986             }
52987         }
52988         this.fireEvent("paneladded", this, panel);
52989         return panel;
52990     },
52991     
52992     /**
52993      * Returns true if the panel is in this region.
52994      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
52995      * @return {Boolean}
52996      */
52997     hasPanel : function(panel){
52998         if(typeof panel == "object"){ // must be panel obj
52999             panel = panel.getId();
53000         }
53001         return this.getPanel(panel) ? true : false;
53002     },
53003     
53004     /**
53005      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53006      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53007      * @param {Boolean} preservePanel Overrides the config preservePanel option
53008      * @return {Roo.ContentPanel} The panel that was removed
53009      */
53010     remove : function(panel, preservePanel){
53011         panel = this.getPanel(panel);
53012         if(!panel){
53013             return null;
53014         }
53015         var e = {};
53016         this.fireEvent("beforeremove", this, panel, e);
53017         if(e.cancel === true){
53018             return null;
53019         }
53020         var panelId = panel.getId();
53021         this.panels.removeKey(panelId);
53022         return panel;
53023     },
53024     
53025     /**
53026      * Returns the panel specified or null if it's not in this region.
53027      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53028      * @return {Roo.ContentPanel}
53029      */
53030     getPanel : function(id){
53031         if(typeof id == "object"){ // must be panel obj
53032             return id;
53033         }
53034         return this.panels.get(id);
53035     },
53036     
53037     /**
53038      * Returns this regions position (north/south/east/west/center).
53039      * @return {String} 
53040      */
53041     getPosition: function(){
53042         return this.position;    
53043     }
53044 });/*
53045  * Based on:
53046  * Ext JS Library 1.1.1
53047  * Copyright(c) 2006-2007, Ext JS, LLC.
53048  *
53049  * Originally Released Under LGPL - original licence link has changed is not relivant.
53050  *
53051  * Fork - LGPL
53052  * <script type="text/javascript">
53053  */
53054  
53055 /**
53056  * @class Roo.LayoutRegion
53057  * @extends Roo.BasicLayoutRegion
53058  * This class represents a region in a layout manager.
53059  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53060  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53061  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53062  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53063  * @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})
53064  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53065  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53066  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53067  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53068  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53069  * @cfg {String}    title           The title for the region (overrides panel titles)
53070  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53071  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53072  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53073  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53074  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53075  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53076  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53077  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53078  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53079  * @cfg {Boolean}   showPin         True to show a pin button
53080  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53081  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53082  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53083  * @cfg {Number}    width           For East/West panels
53084  * @cfg {Number}    height          For North/South panels
53085  * @cfg {Boolean}   split           To show the splitter
53086  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53087  */
53088 Roo.LayoutRegion = function(mgr, config, pos){
53089     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53090     var dh = Roo.DomHelper;
53091     /** This region's container element 
53092     * @type Roo.Element */
53093     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53094     /** This region's title element 
53095     * @type Roo.Element */
53096
53097     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53098         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53099         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53100     ]}, true);
53101     this.titleEl.enableDisplayMode();
53102     /** This region's title text element 
53103     * @type HTMLElement */
53104     this.titleTextEl = this.titleEl.dom.firstChild;
53105     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53106     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53107     this.closeBtn.enableDisplayMode();
53108     this.closeBtn.on("click", this.closeClicked, this);
53109     this.closeBtn.hide();
53110
53111     this.createBody(config);
53112     this.visible = true;
53113     this.collapsed = false;
53114
53115     if(config.hideWhenEmpty){
53116         this.hide();
53117         this.on("paneladded", this.validateVisibility, this);
53118         this.on("panelremoved", this.validateVisibility, this);
53119     }
53120     this.applyConfig(config);
53121 };
53122
53123 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53124
53125     createBody : function(){
53126         /** This region's body element 
53127         * @type Roo.Element */
53128         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53129     },
53130
53131     applyConfig : function(c){
53132         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53133             var dh = Roo.DomHelper;
53134             if(c.titlebar !== false){
53135                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53136                 this.collapseBtn.on("click", this.collapse, this);
53137                 this.collapseBtn.enableDisplayMode();
53138
53139                 if(c.showPin === true || this.showPin){
53140                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53141                     this.stickBtn.enableDisplayMode();
53142                     this.stickBtn.on("click", this.expand, this);
53143                     this.stickBtn.hide();
53144                 }
53145             }
53146             /** This region's collapsed element
53147             * @type Roo.Element */
53148             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53149                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53150             ]}, true);
53151             if(c.floatable !== false){
53152                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53153                this.collapsedEl.on("click", this.collapseClick, this);
53154             }
53155
53156             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53157                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53158                    id: "message", unselectable: "on", style:{"float":"left"}});
53159                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53160              }
53161             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53162             this.expandBtn.on("click", this.expand, this);
53163         }
53164         if(this.collapseBtn){
53165             this.collapseBtn.setVisible(c.collapsible == true);
53166         }
53167         this.cmargins = c.cmargins || this.cmargins ||
53168                          (this.position == "west" || this.position == "east" ?
53169                              {top: 0, left: 2, right:2, bottom: 0} :
53170                              {top: 2, left: 0, right:0, bottom: 2});
53171         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53172         this.bottomTabs = c.tabPosition != "top";
53173         this.autoScroll = c.autoScroll || false;
53174         if(this.autoScroll){
53175             this.bodyEl.setStyle("overflow", "auto");
53176         }else{
53177             this.bodyEl.setStyle("overflow", "hidden");
53178         }
53179         //if(c.titlebar !== false){
53180             if((!c.titlebar && !c.title) || c.titlebar === false){
53181                 this.titleEl.hide();
53182             }else{
53183                 this.titleEl.show();
53184                 if(c.title){
53185                     this.titleTextEl.innerHTML = c.title;
53186                 }
53187             }
53188         //}
53189         this.duration = c.duration || .30;
53190         this.slideDuration = c.slideDuration || .45;
53191         this.config = c;
53192         if(c.collapsed){
53193             this.collapse(true);
53194         }
53195         if(c.hidden){
53196             this.hide();
53197         }
53198     },
53199     /**
53200      * Returns true if this region is currently visible.
53201      * @return {Boolean}
53202      */
53203     isVisible : function(){
53204         return this.visible;
53205     },
53206
53207     /**
53208      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53209      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53210      */
53211     setCollapsedTitle : function(title){
53212         title = title || "&#160;";
53213         if(this.collapsedTitleTextEl){
53214             this.collapsedTitleTextEl.innerHTML = title;
53215         }
53216     },
53217
53218     getBox : function(){
53219         var b;
53220         if(!this.collapsed){
53221             b = this.el.getBox(false, true);
53222         }else{
53223             b = this.collapsedEl.getBox(false, true);
53224         }
53225         return b;
53226     },
53227
53228     getMargins : function(){
53229         return this.collapsed ? this.cmargins : this.margins;
53230     },
53231
53232     highlight : function(){
53233         this.el.addClass("x-layout-panel-dragover");
53234     },
53235
53236     unhighlight : function(){
53237         this.el.removeClass("x-layout-panel-dragover");
53238     },
53239
53240     updateBox : function(box){
53241         this.box = box;
53242         if(!this.collapsed){
53243             this.el.dom.style.left = box.x + "px";
53244             this.el.dom.style.top = box.y + "px";
53245             this.updateBody(box.width, box.height);
53246         }else{
53247             this.collapsedEl.dom.style.left = box.x + "px";
53248             this.collapsedEl.dom.style.top = box.y + "px";
53249             this.collapsedEl.setSize(box.width, box.height);
53250         }
53251         if(this.tabs){
53252             this.tabs.autoSizeTabs();
53253         }
53254     },
53255
53256     updateBody : function(w, h){
53257         if(w !== null){
53258             this.el.setWidth(w);
53259             w -= this.el.getBorderWidth("rl");
53260             if(this.config.adjustments){
53261                 w += this.config.adjustments[0];
53262             }
53263         }
53264         if(h !== null){
53265             this.el.setHeight(h);
53266             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53267             h -= this.el.getBorderWidth("tb");
53268             if(this.config.adjustments){
53269                 h += this.config.adjustments[1];
53270             }
53271             this.bodyEl.setHeight(h);
53272             if(this.tabs){
53273                 h = this.tabs.syncHeight(h);
53274             }
53275         }
53276         if(this.panelSize){
53277             w = w !== null ? w : this.panelSize.width;
53278             h = h !== null ? h : this.panelSize.height;
53279         }
53280         if(this.activePanel){
53281             var el = this.activePanel.getEl();
53282             w = w !== null ? w : el.getWidth();
53283             h = h !== null ? h : el.getHeight();
53284             this.panelSize = {width: w, height: h};
53285             this.activePanel.setSize(w, h);
53286         }
53287         if(Roo.isIE && this.tabs){
53288             this.tabs.el.repaint();
53289         }
53290     },
53291
53292     /**
53293      * Returns the container element for this region.
53294      * @return {Roo.Element}
53295      */
53296     getEl : function(){
53297         return this.el;
53298     },
53299
53300     /**
53301      * Hides this region.
53302      */
53303     hide : function(){
53304         if(!this.collapsed){
53305             this.el.dom.style.left = "-2000px";
53306             this.el.hide();
53307         }else{
53308             this.collapsedEl.dom.style.left = "-2000px";
53309             this.collapsedEl.hide();
53310         }
53311         this.visible = false;
53312         this.fireEvent("visibilitychange", this, false);
53313     },
53314
53315     /**
53316      * Shows this region if it was previously hidden.
53317      */
53318     show : function(){
53319         if(!this.collapsed){
53320             this.el.show();
53321         }else{
53322             this.collapsedEl.show();
53323         }
53324         this.visible = true;
53325         this.fireEvent("visibilitychange", this, true);
53326     },
53327
53328     closeClicked : function(){
53329         if(this.activePanel){
53330             this.remove(this.activePanel);
53331         }
53332     },
53333
53334     collapseClick : function(e){
53335         if(this.isSlid){
53336            e.stopPropagation();
53337            this.slideIn();
53338         }else{
53339            e.stopPropagation();
53340            this.slideOut();
53341         }
53342     },
53343
53344     /**
53345      * Collapses this region.
53346      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53347      */
53348     collapse : function(skipAnim, skipCheck){
53349         if(this.collapsed) {
53350             return;
53351         }
53352         
53353         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53354             
53355             this.collapsed = true;
53356             if(this.split){
53357                 this.split.el.hide();
53358             }
53359             if(this.config.animate && skipAnim !== true){
53360                 this.fireEvent("invalidated", this);
53361                 this.animateCollapse();
53362             }else{
53363                 this.el.setLocation(-20000,-20000);
53364                 this.el.hide();
53365                 this.collapsedEl.show();
53366                 this.fireEvent("collapsed", this);
53367                 this.fireEvent("invalidated", this);
53368             }
53369         }
53370         
53371     },
53372
53373     animateCollapse : function(){
53374         // overridden
53375     },
53376
53377     /**
53378      * Expands this region if it was previously collapsed.
53379      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53380      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53381      */
53382     expand : function(e, skipAnim){
53383         if(e) {
53384             e.stopPropagation();
53385         }
53386         if(!this.collapsed || this.el.hasActiveFx()) {
53387             return;
53388         }
53389         if(this.isSlid){
53390             this.afterSlideIn();
53391             skipAnim = true;
53392         }
53393         this.collapsed = false;
53394         if(this.config.animate && skipAnim !== true){
53395             this.animateExpand();
53396         }else{
53397             this.el.show();
53398             if(this.split){
53399                 this.split.el.show();
53400             }
53401             this.collapsedEl.setLocation(-2000,-2000);
53402             this.collapsedEl.hide();
53403             this.fireEvent("invalidated", this);
53404             this.fireEvent("expanded", this);
53405         }
53406     },
53407
53408     animateExpand : function(){
53409         // overridden
53410     },
53411
53412     initTabs : function()
53413     {
53414         this.bodyEl.setStyle("overflow", "hidden");
53415         var ts = new Roo.TabPanel(
53416                 this.bodyEl.dom,
53417                 {
53418                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53419                     disableTooltips: this.config.disableTabTips,
53420                     toolbar : this.config.toolbar
53421                 }
53422         );
53423         if(this.config.hideTabs){
53424             ts.stripWrap.setDisplayed(false);
53425         }
53426         this.tabs = ts;
53427         ts.resizeTabs = this.config.resizeTabs === true;
53428         ts.minTabWidth = this.config.minTabWidth || 40;
53429         ts.maxTabWidth = this.config.maxTabWidth || 250;
53430         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53431         ts.monitorResize = false;
53432         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53433         ts.bodyEl.addClass('x-layout-tabs-body');
53434         this.panels.each(this.initPanelAsTab, this);
53435     },
53436
53437     initPanelAsTab : function(panel){
53438         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53439                     this.config.closeOnTab && panel.isClosable());
53440         if(panel.tabTip !== undefined){
53441             ti.setTooltip(panel.tabTip);
53442         }
53443         ti.on("activate", function(){
53444               this.setActivePanel(panel);
53445         }, this);
53446         if(this.config.closeOnTab){
53447             ti.on("beforeclose", function(t, e){
53448                 e.cancel = true;
53449                 this.remove(panel);
53450             }, this);
53451         }
53452         return ti;
53453     },
53454
53455     updatePanelTitle : function(panel, title){
53456         if(this.activePanel == panel){
53457             this.updateTitle(title);
53458         }
53459         if(this.tabs){
53460             var ti = this.tabs.getTab(panel.getEl().id);
53461             ti.setText(title);
53462             if(panel.tabTip !== undefined){
53463                 ti.setTooltip(panel.tabTip);
53464             }
53465         }
53466     },
53467
53468     updateTitle : function(title){
53469         if(this.titleTextEl && !this.config.title){
53470             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53471         }
53472     },
53473
53474     setActivePanel : function(panel){
53475         panel = this.getPanel(panel);
53476         if(this.activePanel && this.activePanel != panel){
53477             this.activePanel.setActiveState(false);
53478         }
53479         this.activePanel = panel;
53480         panel.setActiveState(true);
53481         if(this.panelSize){
53482             panel.setSize(this.panelSize.width, this.panelSize.height);
53483         }
53484         if(this.closeBtn){
53485             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53486         }
53487         this.updateTitle(panel.getTitle());
53488         if(this.tabs){
53489             this.fireEvent("invalidated", this);
53490         }
53491         this.fireEvent("panelactivated", this, panel);
53492     },
53493
53494     /**
53495      * Shows the specified panel.
53496      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53497      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53498      */
53499     showPanel : function(panel)
53500     {
53501         panel = this.getPanel(panel);
53502         if(panel){
53503             if(this.tabs){
53504                 var tab = this.tabs.getTab(panel.getEl().id);
53505                 if(tab.isHidden()){
53506                     this.tabs.unhideTab(tab.id);
53507                 }
53508                 tab.activate();
53509             }else{
53510                 this.setActivePanel(panel);
53511             }
53512         }
53513         return panel;
53514     },
53515
53516     /**
53517      * Get the active panel for this region.
53518      * @return {Roo.ContentPanel} The active panel or null
53519      */
53520     getActivePanel : function(){
53521         return this.activePanel;
53522     },
53523
53524     validateVisibility : function(){
53525         if(this.panels.getCount() < 1){
53526             this.updateTitle("&#160;");
53527             this.closeBtn.hide();
53528             this.hide();
53529         }else{
53530             if(!this.isVisible()){
53531                 this.show();
53532             }
53533         }
53534     },
53535
53536     /**
53537      * Adds the passed ContentPanel(s) to this region.
53538      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53539      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53540      */
53541     add : function(panel){
53542         if(arguments.length > 1){
53543             for(var i = 0, len = arguments.length; i < len; i++) {
53544                 this.add(arguments[i]);
53545             }
53546             return null;
53547         }
53548         if(this.hasPanel(panel)){
53549             this.showPanel(panel);
53550             return panel;
53551         }
53552         panel.setRegion(this);
53553         this.panels.add(panel);
53554         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53555             this.bodyEl.dom.appendChild(panel.getEl().dom);
53556             if(panel.background !== true){
53557                 this.setActivePanel(panel);
53558             }
53559             this.fireEvent("paneladded", this, panel);
53560             return panel;
53561         }
53562         if(!this.tabs){
53563             this.initTabs();
53564         }else{
53565             this.initPanelAsTab(panel);
53566         }
53567         if(panel.background !== true){
53568             this.tabs.activate(panel.getEl().id);
53569         }
53570         this.fireEvent("paneladded", this, panel);
53571         return panel;
53572     },
53573
53574     /**
53575      * Hides the tab for the specified panel.
53576      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53577      */
53578     hidePanel : function(panel){
53579         if(this.tabs && (panel = this.getPanel(panel))){
53580             this.tabs.hideTab(panel.getEl().id);
53581         }
53582     },
53583
53584     /**
53585      * Unhides the tab for a previously hidden panel.
53586      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53587      */
53588     unhidePanel : function(panel){
53589         if(this.tabs && (panel = this.getPanel(panel))){
53590             this.tabs.unhideTab(panel.getEl().id);
53591         }
53592     },
53593
53594     clearPanels : function(){
53595         while(this.panels.getCount() > 0){
53596              this.remove(this.panels.first());
53597         }
53598     },
53599
53600     /**
53601      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53602      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53603      * @param {Boolean} preservePanel Overrides the config preservePanel option
53604      * @return {Roo.ContentPanel} The panel that was removed
53605      */
53606     remove : function(panel, preservePanel){
53607         panel = this.getPanel(panel);
53608         if(!panel){
53609             return null;
53610         }
53611         var e = {};
53612         this.fireEvent("beforeremove", this, panel, e);
53613         if(e.cancel === true){
53614             return null;
53615         }
53616         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53617         var panelId = panel.getId();
53618         this.panels.removeKey(panelId);
53619         if(preservePanel){
53620             document.body.appendChild(panel.getEl().dom);
53621         }
53622         if(this.tabs){
53623             this.tabs.removeTab(panel.getEl().id);
53624         }else if (!preservePanel){
53625             this.bodyEl.dom.removeChild(panel.getEl().dom);
53626         }
53627         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53628             var p = this.panels.first();
53629             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53630             tempEl.appendChild(p.getEl().dom);
53631             this.bodyEl.update("");
53632             this.bodyEl.dom.appendChild(p.getEl().dom);
53633             tempEl = null;
53634             this.updateTitle(p.getTitle());
53635             this.tabs = null;
53636             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53637             this.setActivePanel(p);
53638         }
53639         panel.setRegion(null);
53640         if(this.activePanel == panel){
53641             this.activePanel = null;
53642         }
53643         if(this.config.autoDestroy !== false && preservePanel !== true){
53644             try{panel.destroy();}catch(e){}
53645         }
53646         this.fireEvent("panelremoved", this, panel);
53647         return panel;
53648     },
53649
53650     /**
53651      * Returns the TabPanel component used by this region
53652      * @return {Roo.TabPanel}
53653      */
53654     getTabs : function(){
53655         return this.tabs;
53656     },
53657
53658     createTool : function(parentEl, className){
53659         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53660             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53661         btn.addClassOnOver("x-layout-tools-button-over");
53662         return btn;
53663     }
53664 });/*
53665  * Based on:
53666  * Ext JS Library 1.1.1
53667  * Copyright(c) 2006-2007, Ext JS, LLC.
53668  *
53669  * Originally Released Under LGPL - original licence link has changed is not relivant.
53670  *
53671  * Fork - LGPL
53672  * <script type="text/javascript">
53673  */
53674  
53675
53676
53677 /**
53678  * @class Roo.SplitLayoutRegion
53679  * @extends Roo.LayoutRegion
53680  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53681  */
53682 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53683     this.cursor = cursor;
53684     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53685 };
53686
53687 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53688     splitTip : "Drag to resize.",
53689     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53690     useSplitTips : false,
53691
53692     applyConfig : function(config){
53693         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53694         if(config.split){
53695             if(!this.split){
53696                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53697                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53698                 /** The SplitBar for this region 
53699                 * @type Roo.SplitBar */
53700                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53701                 this.split.on("moved", this.onSplitMove, this);
53702                 this.split.useShim = config.useShim === true;
53703                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53704                 if(this.useSplitTips){
53705                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53706                 }
53707                 if(config.collapsible){
53708                     this.split.el.on("dblclick", this.collapse,  this);
53709                 }
53710             }
53711             if(typeof config.minSize != "undefined"){
53712                 this.split.minSize = config.minSize;
53713             }
53714             if(typeof config.maxSize != "undefined"){
53715                 this.split.maxSize = config.maxSize;
53716             }
53717             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53718                 this.hideSplitter();
53719             }
53720         }
53721     },
53722
53723     getHMaxSize : function(){
53724          var cmax = this.config.maxSize || 10000;
53725          var center = this.mgr.getRegion("center");
53726          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53727     },
53728
53729     getVMaxSize : function(){
53730          var cmax = this.config.maxSize || 10000;
53731          var center = this.mgr.getRegion("center");
53732          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53733     },
53734
53735     onSplitMove : function(split, newSize){
53736         this.fireEvent("resized", this, newSize);
53737     },
53738     
53739     /** 
53740      * Returns the {@link Roo.SplitBar} for this region.
53741      * @return {Roo.SplitBar}
53742      */
53743     getSplitBar : function(){
53744         return this.split;
53745     },
53746     
53747     hide : function(){
53748         this.hideSplitter();
53749         Roo.SplitLayoutRegion.superclass.hide.call(this);
53750     },
53751
53752     hideSplitter : function(){
53753         if(this.split){
53754             this.split.el.setLocation(-2000,-2000);
53755             this.split.el.hide();
53756         }
53757     },
53758
53759     show : function(){
53760         if(this.split){
53761             this.split.el.show();
53762         }
53763         Roo.SplitLayoutRegion.superclass.show.call(this);
53764     },
53765     
53766     beforeSlide: function(){
53767         if(Roo.isGecko){// firefox overflow auto bug workaround
53768             this.bodyEl.clip();
53769             if(this.tabs) {
53770                 this.tabs.bodyEl.clip();
53771             }
53772             if(this.activePanel){
53773                 this.activePanel.getEl().clip();
53774                 
53775                 if(this.activePanel.beforeSlide){
53776                     this.activePanel.beforeSlide();
53777                 }
53778             }
53779         }
53780     },
53781     
53782     afterSlide : function(){
53783         if(Roo.isGecko){// firefox overflow auto bug workaround
53784             this.bodyEl.unclip();
53785             if(this.tabs) {
53786                 this.tabs.bodyEl.unclip();
53787             }
53788             if(this.activePanel){
53789                 this.activePanel.getEl().unclip();
53790                 if(this.activePanel.afterSlide){
53791                     this.activePanel.afterSlide();
53792                 }
53793             }
53794         }
53795     },
53796
53797     initAutoHide : function(){
53798         if(this.autoHide !== false){
53799             if(!this.autoHideHd){
53800                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53801                 this.autoHideHd = {
53802                     "mouseout": function(e){
53803                         if(!e.within(this.el, true)){
53804                             st.delay(500);
53805                         }
53806                     },
53807                     "mouseover" : function(e){
53808                         st.cancel();
53809                     },
53810                     scope : this
53811                 };
53812             }
53813             this.el.on(this.autoHideHd);
53814         }
53815     },
53816
53817     clearAutoHide : function(){
53818         if(this.autoHide !== false){
53819             this.el.un("mouseout", this.autoHideHd.mouseout);
53820             this.el.un("mouseover", this.autoHideHd.mouseover);
53821         }
53822     },
53823
53824     clearMonitor : function(){
53825         Roo.get(document).un("click", this.slideInIf, this);
53826     },
53827
53828     // these names are backwards but not changed for compat
53829     slideOut : function(){
53830         if(this.isSlid || this.el.hasActiveFx()){
53831             return;
53832         }
53833         this.isSlid = true;
53834         if(this.collapseBtn){
53835             this.collapseBtn.hide();
53836         }
53837         this.closeBtnState = this.closeBtn.getStyle('display');
53838         this.closeBtn.hide();
53839         if(this.stickBtn){
53840             this.stickBtn.show();
53841         }
53842         this.el.show();
53843         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53844         this.beforeSlide();
53845         this.el.setStyle("z-index", 10001);
53846         this.el.slideIn(this.getSlideAnchor(), {
53847             callback: function(){
53848                 this.afterSlide();
53849                 this.initAutoHide();
53850                 Roo.get(document).on("click", this.slideInIf, this);
53851                 this.fireEvent("slideshow", this);
53852             },
53853             scope: this,
53854             block: true
53855         });
53856     },
53857
53858     afterSlideIn : function(){
53859         this.clearAutoHide();
53860         this.isSlid = false;
53861         this.clearMonitor();
53862         this.el.setStyle("z-index", "");
53863         if(this.collapseBtn){
53864             this.collapseBtn.show();
53865         }
53866         this.closeBtn.setStyle('display', this.closeBtnState);
53867         if(this.stickBtn){
53868             this.stickBtn.hide();
53869         }
53870         this.fireEvent("slidehide", this);
53871     },
53872
53873     slideIn : function(cb){
53874         if(!this.isSlid || this.el.hasActiveFx()){
53875             Roo.callback(cb);
53876             return;
53877         }
53878         this.isSlid = false;
53879         this.beforeSlide();
53880         this.el.slideOut(this.getSlideAnchor(), {
53881             callback: function(){
53882                 this.el.setLeftTop(-10000, -10000);
53883                 this.afterSlide();
53884                 this.afterSlideIn();
53885                 Roo.callback(cb);
53886             },
53887             scope: this,
53888             block: true
53889         });
53890     },
53891     
53892     slideInIf : function(e){
53893         if(!e.within(this.el)){
53894             this.slideIn();
53895         }
53896     },
53897
53898     animateCollapse : function(){
53899         this.beforeSlide();
53900         this.el.setStyle("z-index", 20000);
53901         var anchor = this.getSlideAnchor();
53902         this.el.slideOut(anchor, {
53903             callback : function(){
53904                 this.el.setStyle("z-index", "");
53905                 this.collapsedEl.slideIn(anchor, {duration:.3});
53906                 this.afterSlide();
53907                 this.el.setLocation(-10000,-10000);
53908                 this.el.hide();
53909                 this.fireEvent("collapsed", this);
53910             },
53911             scope: this,
53912             block: true
53913         });
53914     },
53915
53916     animateExpand : function(){
53917         this.beforeSlide();
53918         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53919         this.el.setStyle("z-index", 20000);
53920         this.collapsedEl.hide({
53921             duration:.1
53922         });
53923         this.el.slideIn(this.getSlideAnchor(), {
53924             callback : function(){
53925                 this.el.setStyle("z-index", "");
53926                 this.afterSlide();
53927                 if(this.split){
53928                     this.split.el.show();
53929                 }
53930                 this.fireEvent("invalidated", this);
53931                 this.fireEvent("expanded", this);
53932             },
53933             scope: this,
53934             block: true
53935         });
53936     },
53937
53938     anchors : {
53939         "west" : "left",
53940         "east" : "right",
53941         "north" : "top",
53942         "south" : "bottom"
53943     },
53944
53945     sanchors : {
53946         "west" : "l",
53947         "east" : "r",
53948         "north" : "t",
53949         "south" : "b"
53950     },
53951
53952     canchors : {
53953         "west" : "tl-tr",
53954         "east" : "tr-tl",
53955         "north" : "tl-bl",
53956         "south" : "bl-tl"
53957     },
53958
53959     getAnchor : function(){
53960         return this.anchors[this.position];
53961     },
53962
53963     getCollapseAnchor : function(){
53964         return this.canchors[this.position];
53965     },
53966
53967     getSlideAnchor : function(){
53968         return this.sanchors[this.position];
53969     },
53970
53971     getAlignAdj : function(){
53972         var cm = this.cmargins;
53973         switch(this.position){
53974             case "west":
53975                 return [0, 0];
53976             break;
53977             case "east":
53978                 return [0, 0];
53979             break;
53980             case "north":
53981                 return [0, 0];
53982             break;
53983             case "south":
53984                 return [0, 0];
53985             break;
53986         }
53987     },
53988
53989     getExpandAdj : function(){
53990         var c = this.collapsedEl, cm = this.cmargins;
53991         switch(this.position){
53992             case "west":
53993                 return [-(cm.right+c.getWidth()+cm.left), 0];
53994             break;
53995             case "east":
53996                 return [cm.right+c.getWidth()+cm.left, 0];
53997             break;
53998             case "north":
53999                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54000             break;
54001             case "south":
54002                 return [0, cm.top+cm.bottom+c.getHeight()];
54003             break;
54004         }
54005     }
54006 });/*
54007  * Based on:
54008  * Ext JS Library 1.1.1
54009  * Copyright(c) 2006-2007, Ext JS, LLC.
54010  *
54011  * Originally Released Under LGPL - original licence link has changed is not relivant.
54012  *
54013  * Fork - LGPL
54014  * <script type="text/javascript">
54015  */
54016 /*
54017  * These classes are private internal classes
54018  */
54019 Roo.CenterLayoutRegion = function(mgr, config){
54020     Roo.LayoutRegion.call(this, mgr, config, "center");
54021     this.visible = true;
54022     this.minWidth = config.minWidth || 20;
54023     this.minHeight = config.minHeight || 20;
54024 };
54025
54026 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54027     hide : function(){
54028         // center panel can't be hidden
54029     },
54030     
54031     show : function(){
54032         // center panel can't be hidden
54033     },
54034     
54035     getMinWidth: function(){
54036         return this.minWidth;
54037     },
54038     
54039     getMinHeight: function(){
54040         return this.minHeight;
54041     }
54042 });
54043
54044
54045 Roo.NorthLayoutRegion = function(mgr, config){
54046     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54047     if(this.split){
54048         this.split.placement = Roo.SplitBar.TOP;
54049         this.split.orientation = Roo.SplitBar.VERTICAL;
54050         this.split.el.addClass("x-layout-split-v");
54051     }
54052     var size = config.initialSize || config.height;
54053     if(typeof size != "undefined"){
54054         this.el.setHeight(size);
54055     }
54056 };
54057 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54058     orientation: Roo.SplitBar.VERTICAL,
54059     getBox : function(){
54060         if(this.collapsed){
54061             return this.collapsedEl.getBox();
54062         }
54063         var box = this.el.getBox();
54064         if(this.split){
54065             box.height += this.split.el.getHeight();
54066         }
54067         return box;
54068     },
54069     
54070     updateBox : function(box){
54071         if(this.split && !this.collapsed){
54072             box.height -= this.split.el.getHeight();
54073             this.split.el.setLeft(box.x);
54074             this.split.el.setTop(box.y+box.height);
54075             this.split.el.setWidth(box.width);
54076         }
54077         if(this.collapsed){
54078             this.updateBody(box.width, null);
54079         }
54080         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54081     }
54082 });
54083
54084 Roo.SouthLayoutRegion = function(mgr, config){
54085     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54086     if(this.split){
54087         this.split.placement = Roo.SplitBar.BOTTOM;
54088         this.split.orientation = Roo.SplitBar.VERTICAL;
54089         this.split.el.addClass("x-layout-split-v");
54090     }
54091     var size = config.initialSize || config.height;
54092     if(typeof size != "undefined"){
54093         this.el.setHeight(size);
54094     }
54095 };
54096 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54097     orientation: Roo.SplitBar.VERTICAL,
54098     getBox : function(){
54099         if(this.collapsed){
54100             return this.collapsedEl.getBox();
54101         }
54102         var box = this.el.getBox();
54103         if(this.split){
54104             var sh = this.split.el.getHeight();
54105             box.height += sh;
54106             box.y -= sh;
54107         }
54108         return box;
54109     },
54110     
54111     updateBox : function(box){
54112         if(this.split && !this.collapsed){
54113             var sh = this.split.el.getHeight();
54114             box.height -= sh;
54115             box.y += sh;
54116             this.split.el.setLeft(box.x);
54117             this.split.el.setTop(box.y-sh);
54118             this.split.el.setWidth(box.width);
54119         }
54120         if(this.collapsed){
54121             this.updateBody(box.width, null);
54122         }
54123         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54124     }
54125 });
54126
54127 Roo.EastLayoutRegion = function(mgr, config){
54128     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54129     if(this.split){
54130         this.split.placement = Roo.SplitBar.RIGHT;
54131         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54132         this.split.el.addClass("x-layout-split-h");
54133     }
54134     var size = config.initialSize || config.width;
54135     if(typeof size != "undefined"){
54136         this.el.setWidth(size);
54137     }
54138 };
54139 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54140     orientation: Roo.SplitBar.HORIZONTAL,
54141     getBox : function(){
54142         if(this.collapsed){
54143             return this.collapsedEl.getBox();
54144         }
54145         var box = this.el.getBox();
54146         if(this.split){
54147             var sw = this.split.el.getWidth();
54148             box.width += sw;
54149             box.x -= sw;
54150         }
54151         return box;
54152     },
54153
54154     updateBox : function(box){
54155         if(this.split && !this.collapsed){
54156             var sw = this.split.el.getWidth();
54157             box.width -= sw;
54158             this.split.el.setLeft(box.x);
54159             this.split.el.setTop(box.y);
54160             this.split.el.setHeight(box.height);
54161             box.x += sw;
54162         }
54163         if(this.collapsed){
54164             this.updateBody(null, box.height);
54165         }
54166         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54167     }
54168 });
54169
54170 Roo.WestLayoutRegion = function(mgr, config){
54171     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54172     if(this.split){
54173         this.split.placement = Roo.SplitBar.LEFT;
54174         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54175         this.split.el.addClass("x-layout-split-h");
54176     }
54177     var size = config.initialSize || config.width;
54178     if(typeof size != "undefined"){
54179         this.el.setWidth(size);
54180     }
54181 };
54182 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54183     orientation: Roo.SplitBar.HORIZONTAL,
54184     getBox : function(){
54185         if(this.collapsed){
54186             return this.collapsedEl.getBox();
54187         }
54188         var box = this.el.getBox();
54189         if(this.split){
54190             box.width += this.split.el.getWidth();
54191         }
54192         return box;
54193     },
54194     
54195     updateBox : function(box){
54196         if(this.split && !this.collapsed){
54197             var sw = this.split.el.getWidth();
54198             box.width -= sw;
54199             this.split.el.setLeft(box.x+box.width);
54200             this.split.el.setTop(box.y);
54201             this.split.el.setHeight(box.height);
54202         }
54203         if(this.collapsed){
54204             this.updateBody(null, box.height);
54205         }
54206         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54207     }
54208 });
54209 /*
54210  * Based on:
54211  * Ext JS Library 1.1.1
54212  * Copyright(c) 2006-2007, Ext JS, LLC.
54213  *
54214  * Originally Released Under LGPL - original licence link has changed is not relivant.
54215  *
54216  * Fork - LGPL
54217  * <script type="text/javascript">
54218  */
54219  
54220  
54221 /*
54222  * Private internal class for reading and applying state
54223  */
54224 Roo.LayoutStateManager = function(layout){
54225      // default empty state
54226      this.state = {
54227         north: {},
54228         south: {},
54229         east: {},
54230         west: {}       
54231     };
54232 };
54233
54234 Roo.LayoutStateManager.prototype = {
54235     init : function(layout, provider){
54236         this.provider = provider;
54237         var state = provider.get(layout.id+"-layout-state");
54238         if(state){
54239             var wasUpdating = layout.isUpdating();
54240             if(!wasUpdating){
54241                 layout.beginUpdate();
54242             }
54243             for(var key in state){
54244                 if(typeof state[key] != "function"){
54245                     var rstate = state[key];
54246                     var r = layout.getRegion(key);
54247                     if(r && rstate){
54248                         if(rstate.size){
54249                             r.resizeTo(rstate.size);
54250                         }
54251                         if(rstate.collapsed == true){
54252                             r.collapse(true);
54253                         }else{
54254                             r.expand(null, true);
54255                         }
54256                     }
54257                 }
54258             }
54259             if(!wasUpdating){
54260                 layout.endUpdate();
54261             }
54262             this.state = state; 
54263         }
54264         this.layout = layout;
54265         layout.on("regionresized", this.onRegionResized, this);
54266         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54267         layout.on("regionexpanded", this.onRegionExpanded, this);
54268     },
54269     
54270     storeState : function(){
54271         this.provider.set(this.layout.id+"-layout-state", this.state);
54272     },
54273     
54274     onRegionResized : function(region, newSize){
54275         this.state[region.getPosition()].size = newSize;
54276         this.storeState();
54277     },
54278     
54279     onRegionCollapsed : function(region){
54280         this.state[region.getPosition()].collapsed = true;
54281         this.storeState();
54282     },
54283     
54284     onRegionExpanded : function(region){
54285         this.state[region.getPosition()].collapsed = false;
54286         this.storeState();
54287     }
54288 };/*
54289  * Based on:
54290  * Ext JS Library 1.1.1
54291  * Copyright(c) 2006-2007, Ext JS, LLC.
54292  *
54293  * Originally Released Under LGPL - original licence link has changed is not relivant.
54294  *
54295  * Fork - LGPL
54296  * <script type="text/javascript">
54297  */
54298 /**
54299  * @class Roo.ContentPanel
54300  * @extends Roo.util.Observable
54301  * A basic ContentPanel element.
54302  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54303  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54304  * @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
54305  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54306  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54307  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54308  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54309  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54310  * @cfg {String} title          The title for this panel
54311  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54312  * @cfg {String} url            Calls {@link #setUrl} with this value
54313  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54314  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54315  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54316  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54317  * @cfg {String}    style  Extra style to add to the content panel 
54318
54319  * @constructor
54320  * Create a new ContentPanel.
54321  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54322  * @param {String/Object} config A string to set only the title or a config object
54323  * @param {String} content (optional) Set the HTML content for this panel
54324  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54325  */
54326 Roo.ContentPanel = function(el, config, content){
54327     
54328      
54329     /*
54330     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54331         config = el;
54332         el = Roo.id();
54333     }
54334     if (config && config.parentLayout) { 
54335         el = config.parentLayout.el.createChild(); 
54336     }
54337     */
54338     if(el.autoCreate){ // xtype is available if this is called from factory
54339         config = el;
54340         el = Roo.id();
54341     }
54342     this.el = Roo.get(el);
54343     if(!this.el && config && config.autoCreate){
54344         if(typeof config.autoCreate == "object"){
54345             if(!config.autoCreate.id){
54346                 config.autoCreate.id = config.id||el;
54347             }
54348             this.el = Roo.DomHelper.append(document.body,
54349                         config.autoCreate, true);
54350         }else{
54351             this.el = Roo.DomHelper.append(document.body,
54352                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54353         }
54354     }
54355     
54356     
54357     this.closable = false;
54358     this.loaded = false;
54359     this.active = false;
54360     if(typeof config == "string"){
54361         this.title = config;
54362     }else{
54363         Roo.apply(this, config);
54364     }
54365     
54366     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54367         this.wrapEl = this.el.wrap();
54368         this.toolbar.container = this.el.insertSibling(false, 'before');
54369         this.toolbar = new Roo.Toolbar(this.toolbar);
54370     }
54371     
54372     // xtype created footer. - not sure if will work as we normally have to render first..
54373     if (this.footer && !this.footer.el && this.footer.xtype) {
54374         if (!this.wrapEl) {
54375             this.wrapEl = this.el.wrap();
54376         }
54377     
54378         this.footer.container = this.wrapEl.createChild();
54379          
54380         this.footer = Roo.factory(this.footer, Roo);
54381         
54382     }
54383     
54384     if(this.resizeEl){
54385         this.resizeEl = Roo.get(this.resizeEl, true);
54386     }else{
54387         this.resizeEl = this.el;
54388     }
54389     // handle view.xtype
54390     
54391  
54392     
54393     
54394     this.addEvents({
54395         /**
54396          * @event activate
54397          * Fires when this panel is activated. 
54398          * @param {Roo.ContentPanel} this
54399          */
54400         "activate" : true,
54401         /**
54402          * @event deactivate
54403          * Fires when this panel is activated. 
54404          * @param {Roo.ContentPanel} this
54405          */
54406         "deactivate" : true,
54407
54408         /**
54409          * @event resize
54410          * Fires when this panel is resized if fitToFrame is true.
54411          * @param {Roo.ContentPanel} this
54412          * @param {Number} width The width after any component adjustments
54413          * @param {Number} height The height after any component adjustments
54414          */
54415         "resize" : true,
54416         
54417          /**
54418          * @event render
54419          * Fires when this tab is created
54420          * @param {Roo.ContentPanel} this
54421          */
54422         "render" : true
54423          
54424         
54425     });
54426     
54427
54428     
54429     
54430     if(this.autoScroll){
54431         this.resizeEl.setStyle("overflow", "auto");
54432     } else {
54433         // fix randome scrolling
54434         this.el.on('scroll', function() {
54435             Roo.log('fix random scolling');
54436             this.scrollTo('top',0); 
54437         });
54438     }
54439     content = content || this.content;
54440     if(content){
54441         this.setContent(content);
54442     }
54443     if(config && config.url){
54444         this.setUrl(this.url, this.params, this.loadOnce);
54445     }
54446     
54447     
54448     
54449     Roo.ContentPanel.superclass.constructor.call(this);
54450     
54451     if (this.view && typeof(this.view.xtype) != 'undefined') {
54452         this.view.el = this.el.appendChild(document.createElement("div"));
54453         this.view = Roo.factory(this.view); 
54454         this.view.render  &&  this.view.render(false, '');  
54455     }
54456     
54457     
54458     this.fireEvent('render', this);
54459 };
54460
54461 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54462     tabTip:'',
54463     setRegion : function(region){
54464         this.region = region;
54465         if(region){
54466            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54467         }else{
54468            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54469         } 
54470     },
54471     
54472     /**
54473      * Returns the toolbar for this Panel if one was configured. 
54474      * @return {Roo.Toolbar} 
54475      */
54476     getToolbar : function(){
54477         return this.toolbar;
54478     },
54479     
54480     setActiveState : function(active){
54481         this.active = active;
54482         if(!active){
54483             this.fireEvent("deactivate", this);
54484         }else{
54485             this.fireEvent("activate", this);
54486         }
54487     },
54488     /**
54489      * Updates this panel's element
54490      * @param {String} content The new content
54491      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54492     */
54493     setContent : function(content, loadScripts){
54494         this.el.update(content, loadScripts);
54495     },
54496
54497     ignoreResize : function(w, h){
54498         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54499             return true;
54500         }else{
54501             this.lastSize = {width: w, height: h};
54502             return false;
54503         }
54504     },
54505     /**
54506      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54507      * @return {Roo.UpdateManager} The UpdateManager
54508      */
54509     getUpdateManager : function(){
54510         return this.el.getUpdateManager();
54511     },
54512      /**
54513      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54514      * @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:
54515 <pre><code>
54516 panel.load({
54517     url: "your-url.php",
54518     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54519     callback: yourFunction,
54520     scope: yourObject, //(optional scope)
54521     discardUrl: false,
54522     nocache: false,
54523     text: "Loading...",
54524     timeout: 30,
54525     scripts: false
54526 });
54527 </code></pre>
54528      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54529      * 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.
54530      * @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}
54531      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54532      * @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.
54533      * @return {Roo.ContentPanel} this
54534      */
54535     load : function(){
54536         var um = this.el.getUpdateManager();
54537         um.update.apply(um, arguments);
54538         return this;
54539     },
54540
54541
54542     /**
54543      * 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.
54544      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54545      * @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)
54546      * @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)
54547      * @return {Roo.UpdateManager} The UpdateManager
54548      */
54549     setUrl : function(url, params, loadOnce){
54550         if(this.refreshDelegate){
54551             this.removeListener("activate", this.refreshDelegate);
54552         }
54553         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54554         this.on("activate", this.refreshDelegate);
54555         return this.el.getUpdateManager();
54556     },
54557     
54558     _handleRefresh : function(url, params, loadOnce){
54559         if(!loadOnce || !this.loaded){
54560             var updater = this.el.getUpdateManager();
54561             updater.update(url, params, this._setLoaded.createDelegate(this));
54562         }
54563     },
54564     
54565     _setLoaded : function(){
54566         this.loaded = true;
54567     }, 
54568     
54569     /**
54570      * Returns this panel's id
54571      * @return {String} 
54572      */
54573     getId : function(){
54574         return this.el.id;
54575     },
54576     
54577     /** 
54578      * Returns this panel's element - used by regiosn to add.
54579      * @return {Roo.Element} 
54580      */
54581     getEl : function(){
54582         return this.wrapEl || this.el;
54583     },
54584     
54585     adjustForComponents : function(width, height)
54586     {
54587         //Roo.log('adjustForComponents ');
54588         if(this.resizeEl != this.el){
54589             width -= this.el.getFrameWidth('lr');
54590             height -= this.el.getFrameWidth('tb');
54591         }
54592         if(this.toolbar){
54593             var te = this.toolbar.getEl();
54594             height -= te.getHeight();
54595             te.setWidth(width);
54596         }
54597         if(this.footer){
54598             var te = this.footer.getEl();
54599             //Roo.log("footer:" + te.getHeight());
54600             
54601             height -= te.getHeight();
54602             te.setWidth(width);
54603         }
54604         
54605         
54606         if(this.adjustments){
54607             width += this.adjustments[0];
54608             height += this.adjustments[1];
54609         }
54610         return {"width": width, "height": height};
54611     },
54612     
54613     setSize : function(width, height){
54614         if(this.fitToFrame && !this.ignoreResize(width, height)){
54615             if(this.fitContainer && this.resizeEl != this.el){
54616                 this.el.setSize(width, height);
54617             }
54618             var size = this.adjustForComponents(width, height);
54619             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54620             this.fireEvent('resize', this, size.width, size.height);
54621         }
54622     },
54623     
54624     /**
54625      * Returns this panel's title
54626      * @return {String} 
54627      */
54628     getTitle : function(){
54629         return this.title;
54630     },
54631     
54632     /**
54633      * Set this panel's title
54634      * @param {String} title
54635      */
54636     setTitle : function(title){
54637         this.title = title;
54638         if(this.region){
54639             this.region.updatePanelTitle(this, title);
54640         }
54641     },
54642     
54643     /**
54644      * Returns true is this panel was configured to be closable
54645      * @return {Boolean} 
54646      */
54647     isClosable : function(){
54648         return this.closable;
54649     },
54650     
54651     beforeSlide : function(){
54652         this.el.clip();
54653         this.resizeEl.clip();
54654     },
54655     
54656     afterSlide : function(){
54657         this.el.unclip();
54658         this.resizeEl.unclip();
54659     },
54660     
54661     /**
54662      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54663      *   Will fail silently if the {@link #setUrl} method has not been called.
54664      *   This does not activate the panel, just updates its content.
54665      */
54666     refresh : function(){
54667         if(this.refreshDelegate){
54668            this.loaded = false;
54669            this.refreshDelegate();
54670         }
54671     },
54672     
54673     /**
54674      * Destroys this panel
54675      */
54676     destroy : function(){
54677         this.el.removeAllListeners();
54678         var tempEl = document.createElement("span");
54679         tempEl.appendChild(this.el.dom);
54680         tempEl.innerHTML = "";
54681         this.el.remove();
54682         this.el = null;
54683     },
54684     
54685     /**
54686      * form - if the content panel contains a form - this is a reference to it.
54687      * @type {Roo.form.Form}
54688      */
54689     form : false,
54690     /**
54691      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54692      *    This contains a reference to it.
54693      * @type {Roo.View}
54694      */
54695     view : false,
54696     
54697       /**
54698      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54699      * <pre><code>
54700
54701 layout.addxtype({
54702        xtype : 'Form',
54703        items: [ .... ]
54704    }
54705 );
54706
54707 </code></pre>
54708      * @param {Object} cfg Xtype definition of item to add.
54709      */
54710     
54711     addxtype : function(cfg) {
54712         // add form..
54713         if (cfg.xtype.match(/^Form$/)) {
54714             
54715             var el;
54716             //if (this.footer) {
54717             //    el = this.footer.container.insertSibling(false, 'before');
54718             //} else {
54719                 el = this.el.createChild();
54720             //}
54721
54722             this.form = new  Roo.form.Form(cfg);
54723             
54724             
54725             if ( this.form.allItems.length) {
54726                 this.form.render(el.dom);
54727             }
54728             return this.form;
54729         }
54730         // should only have one of theses..
54731         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54732             // views.. should not be just added - used named prop 'view''
54733             
54734             cfg.el = this.el.appendChild(document.createElement("div"));
54735             // factory?
54736             
54737             var ret = new Roo.factory(cfg);
54738              
54739              ret.render && ret.render(false, ''); // render blank..
54740             this.view = ret;
54741             return ret;
54742         }
54743         return false;
54744     }
54745 });
54746
54747 /**
54748  * @class Roo.GridPanel
54749  * @extends Roo.ContentPanel
54750  * @constructor
54751  * Create a new GridPanel.
54752  * @param {Roo.grid.Grid} grid The grid for this panel
54753  * @param {String/Object} config A string to set only the panel's title, or a config object
54754  */
54755 Roo.GridPanel = function(grid, config){
54756     
54757   
54758     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54759         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54760         
54761     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54762     
54763     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54764     
54765     if(this.toolbar){
54766         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54767     }
54768     // xtype created footer. - not sure if will work as we normally have to render first..
54769     if (this.footer && !this.footer.el && this.footer.xtype) {
54770         
54771         this.footer.container = this.grid.getView().getFooterPanel(true);
54772         this.footer.dataSource = this.grid.dataSource;
54773         this.footer = Roo.factory(this.footer, Roo);
54774         
54775     }
54776     
54777     grid.monitorWindowResize = false; // turn off autosizing
54778     grid.autoHeight = false;
54779     grid.autoWidth = false;
54780     this.grid = grid;
54781     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54782 };
54783
54784 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54785     getId : function(){
54786         return this.grid.id;
54787     },
54788     
54789     /**
54790      * Returns the grid for this panel
54791      * @return {Roo.grid.Grid} 
54792      */
54793     getGrid : function(){
54794         return this.grid;    
54795     },
54796     
54797     setSize : function(width, height){
54798         if(!this.ignoreResize(width, height)){
54799             var grid = this.grid;
54800             var size = this.adjustForComponents(width, height);
54801             grid.getGridEl().setSize(size.width, size.height);
54802             grid.autoSize();
54803         }
54804     },
54805     
54806     beforeSlide : function(){
54807         this.grid.getView().scroller.clip();
54808     },
54809     
54810     afterSlide : function(){
54811         this.grid.getView().scroller.unclip();
54812     },
54813     
54814     destroy : function(){
54815         this.grid.destroy();
54816         delete this.grid;
54817         Roo.GridPanel.superclass.destroy.call(this); 
54818     }
54819 });
54820
54821
54822 /**
54823  * @class Roo.NestedLayoutPanel
54824  * @extends Roo.ContentPanel
54825  * @constructor
54826  * Create a new NestedLayoutPanel.
54827  * 
54828  * 
54829  * @param {Roo.BorderLayout} layout The layout for this panel
54830  * @param {String/Object} config A string to set only the title or a config object
54831  */
54832 Roo.NestedLayoutPanel = function(layout, config)
54833 {
54834     // construct with only one argument..
54835     /* FIXME - implement nicer consturctors
54836     if (layout.layout) {
54837         config = layout;
54838         layout = config.layout;
54839         delete config.layout;
54840     }
54841     if (layout.xtype && !layout.getEl) {
54842         // then layout needs constructing..
54843         layout = Roo.factory(layout, Roo);
54844     }
54845     */
54846     
54847     
54848     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54849     
54850     layout.monitorWindowResize = false; // turn off autosizing
54851     this.layout = layout;
54852     this.layout.getEl().addClass("x-layout-nested-layout");
54853     
54854     
54855     
54856     
54857 };
54858
54859 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54860
54861     setSize : function(width, height){
54862         if(!this.ignoreResize(width, height)){
54863             var size = this.adjustForComponents(width, height);
54864             var el = this.layout.getEl();
54865             el.setSize(size.width, size.height);
54866             var touch = el.dom.offsetWidth;
54867             this.layout.layout();
54868             // ie requires a double layout on the first pass
54869             if(Roo.isIE && !this.initialized){
54870                 this.initialized = true;
54871                 this.layout.layout();
54872             }
54873         }
54874     },
54875     
54876     // activate all subpanels if not currently active..
54877     
54878     setActiveState : function(active){
54879         this.active = active;
54880         if(!active){
54881             this.fireEvent("deactivate", this);
54882             return;
54883         }
54884         
54885         this.fireEvent("activate", this);
54886         // not sure if this should happen before or after..
54887         if (!this.layout) {
54888             return; // should not happen..
54889         }
54890         var reg = false;
54891         for (var r in this.layout.regions) {
54892             reg = this.layout.getRegion(r);
54893             if (reg.getActivePanel()) {
54894                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54895                 reg.setActivePanel(reg.getActivePanel());
54896                 continue;
54897             }
54898             if (!reg.panels.length) {
54899                 continue;
54900             }
54901             reg.showPanel(reg.getPanel(0));
54902         }
54903         
54904         
54905         
54906         
54907     },
54908     
54909     /**
54910      * Returns the nested BorderLayout for this panel
54911      * @return {Roo.BorderLayout} 
54912      */
54913     getLayout : function(){
54914         return this.layout;
54915     },
54916     
54917      /**
54918      * Adds a xtype elements to the layout of the nested panel
54919      * <pre><code>
54920
54921 panel.addxtype({
54922        xtype : 'ContentPanel',
54923        region: 'west',
54924        items: [ .... ]
54925    }
54926 );
54927
54928 panel.addxtype({
54929         xtype : 'NestedLayoutPanel',
54930         region: 'west',
54931         layout: {
54932            center: { },
54933            west: { }   
54934         },
54935         items : [ ... list of content panels or nested layout panels.. ]
54936    }
54937 );
54938 </code></pre>
54939      * @param {Object} cfg Xtype definition of item to add.
54940      */
54941     addxtype : function(cfg) {
54942         return this.layout.addxtype(cfg);
54943     
54944     }
54945 });
54946
54947 Roo.ScrollPanel = function(el, config, content){
54948     config = config || {};
54949     config.fitToFrame = true;
54950     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
54951     
54952     this.el.dom.style.overflow = "hidden";
54953     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
54954     this.el.removeClass("x-layout-inactive-content");
54955     this.el.on("mousewheel", this.onWheel, this);
54956
54957     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
54958     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
54959     up.unselectable(); down.unselectable();
54960     up.on("click", this.scrollUp, this);
54961     down.on("click", this.scrollDown, this);
54962     up.addClassOnOver("x-scroller-btn-over");
54963     down.addClassOnOver("x-scroller-btn-over");
54964     up.addClassOnClick("x-scroller-btn-click");
54965     down.addClassOnClick("x-scroller-btn-click");
54966     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
54967
54968     this.resizeEl = this.el;
54969     this.el = wrap; this.up = up; this.down = down;
54970 };
54971
54972 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
54973     increment : 100,
54974     wheelIncrement : 5,
54975     scrollUp : function(){
54976         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
54977     },
54978
54979     scrollDown : function(){
54980         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
54981     },
54982
54983     afterScroll : function(){
54984         var el = this.resizeEl;
54985         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
54986         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54987         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
54988     },
54989
54990     setSize : function(){
54991         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
54992         this.afterScroll();
54993     },
54994
54995     onWheel : function(e){
54996         var d = e.getWheelDelta();
54997         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
54998         this.afterScroll();
54999         e.stopEvent();
55000     },
55001
55002     setContent : function(content, loadScripts){
55003         this.resizeEl.update(content, loadScripts);
55004     }
55005
55006 });
55007
55008
55009
55010
55011
55012
55013
55014
55015
55016 /**
55017  * @class Roo.TreePanel
55018  * @extends Roo.ContentPanel
55019  * @constructor
55020  * Create a new TreePanel. - defaults to fit/scoll contents.
55021  * @param {String/Object} config A string to set only the panel's title, or a config object
55022  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
55023  */
55024 Roo.TreePanel = function(config){
55025     var el = config.el;
55026     var tree = config.tree;
55027     delete config.tree; 
55028     delete config.el; // hopefull!
55029     
55030     // wrapper for IE7 strict & safari scroll issue
55031     
55032     var treeEl = el.createChild();
55033     config.resizeEl = treeEl;
55034     
55035     
55036     
55037     Roo.TreePanel.superclass.constructor.call(this, el, config);
55038  
55039  
55040     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55041     //console.log(tree);
55042     this.on('activate', function()
55043     {
55044         if (this.tree.rendered) {
55045             return;
55046         }
55047         //console.log('render tree');
55048         this.tree.render();
55049     });
55050     // this should not be needed.. - it's actually the 'el' that resizes?
55051     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55052     
55053     //this.on('resize',  function (cp, w, h) {
55054     //        this.tree.innerCt.setWidth(w);
55055     //        this.tree.innerCt.setHeight(h);
55056     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55057     //});
55058
55059         
55060     
55061 };
55062
55063 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55064     fitToFrame : true,
55065     autoScroll : true
55066 });
55067
55068
55069
55070
55071
55072
55073
55074
55075
55076
55077
55078 /*
55079  * Based on:
55080  * Ext JS Library 1.1.1
55081  * Copyright(c) 2006-2007, Ext JS, LLC.
55082  *
55083  * Originally Released Under LGPL - original licence link has changed is not relivant.
55084  *
55085  * Fork - LGPL
55086  * <script type="text/javascript">
55087  */
55088  
55089
55090 /**
55091  * @class Roo.ReaderLayout
55092  * @extends Roo.BorderLayout
55093  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55094  * center region containing two nested regions (a top one for a list view and one for item preview below),
55095  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55096  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55097  * expedites the setup of the overall layout and regions for this common application style.
55098  * Example:
55099  <pre><code>
55100 var reader = new Roo.ReaderLayout();
55101 var CP = Roo.ContentPanel;  // shortcut for adding
55102
55103 reader.beginUpdate();
55104 reader.add("north", new CP("north", "North"));
55105 reader.add("west", new CP("west", {title: "West"}));
55106 reader.add("east", new CP("east", {title: "East"}));
55107
55108 reader.regions.listView.add(new CP("listView", "List"));
55109 reader.regions.preview.add(new CP("preview", "Preview"));
55110 reader.endUpdate();
55111 </code></pre>
55112 * @constructor
55113 * Create a new ReaderLayout
55114 * @param {Object} config Configuration options
55115 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55116 * document.body if omitted)
55117 */
55118 Roo.ReaderLayout = function(config, renderTo){
55119     var c = config || {size:{}};
55120     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55121         north: c.north !== false ? Roo.apply({
55122             split:false,
55123             initialSize: 32,
55124             titlebar: false
55125         }, c.north) : false,
55126         west: c.west !== false ? Roo.apply({
55127             split:true,
55128             initialSize: 200,
55129             minSize: 175,
55130             maxSize: 400,
55131             titlebar: true,
55132             collapsible: true,
55133             animate: true,
55134             margins:{left:5,right:0,bottom:5,top:5},
55135             cmargins:{left:5,right:5,bottom:5,top:5}
55136         }, c.west) : false,
55137         east: c.east !== false ? Roo.apply({
55138             split:true,
55139             initialSize: 200,
55140             minSize: 175,
55141             maxSize: 400,
55142             titlebar: true,
55143             collapsible: true,
55144             animate: true,
55145             margins:{left:0,right:5,bottom:5,top:5},
55146             cmargins:{left:5,right:5,bottom:5,top:5}
55147         }, c.east) : false,
55148         center: Roo.apply({
55149             tabPosition: 'top',
55150             autoScroll:false,
55151             closeOnTab: true,
55152             titlebar:false,
55153             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55154         }, c.center)
55155     });
55156
55157     this.el.addClass('x-reader');
55158
55159     this.beginUpdate();
55160
55161     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55162         south: c.preview !== false ? Roo.apply({
55163             split:true,
55164             initialSize: 200,
55165             minSize: 100,
55166             autoScroll:true,
55167             collapsible:true,
55168             titlebar: true,
55169             cmargins:{top:5,left:0, right:0, bottom:0}
55170         }, c.preview) : false,
55171         center: Roo.apply({
55172             autoScroll:false,
55173             titlebar:false,
55174             minHeight:200
55175         }, c.listView)
55176     });
55177     this.add('center', new Roo.NestedLayoutPanel(inner,
55178             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55179
55180     this.endUpdate();
55181
55182     this.regions.preview = inner.getRegion('south');
55183     this.regions.listView = inner.getRegion('center');
55184 };
55185
55186 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55187  * Based on:
55188  * Ext JS Library 1.1.1
55189  * Copyright(c) 2006-2007, Ext JS, LLC.
55190  *
55191  * Originally Released Under LGPL - original licence link has changed is not relivant.
55192  *
55193  * Fork - LGPL
55194  * <script type="text/javascript">
55195  */
55196  
55197 /**
55198  * @class Roo.grid.Grid
55199  * @extends Roo.util.Observable
55200  * This class represents the primary interface of a component based grid control.
55201  * <br><br>Usage:<pre><code>
55202  var grid = new Roo.grid.Grid("my-container-id", {
55203      ds: myDataStore,
55204      cm: myColModel,
55205      selModel: mySelectionModel,
55206      autoSizeColumns: true,
55207      monitorWindowResize: false,
55208      trackMouseOver: true
55209  });
55210  // set any options
55211  grid.render();
55212  * </code></pre>
55213  * <b>Common Problems:</b><br/>
55214  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55215  * element will correct this<br/>
55216  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55217  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55218  * are unpredictable.<br/>
55219  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55220  * grid to calculate dimensions/offsets.<br/>
55221   * @constructor
55222  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55223  * The container MUST have some type of size defined for the grid to fill. The container will be
55224  * automatically set to position relative if it isn't already.
55225  * @param {Object} config A config object that sets properties on this grid.
55226  */
55227 Roo.grid.Grid = function(container, config){
55228         // initialize the container
55229         this.container = Roo.get(container);
55230         this.container.update("");
55231         this.container.setStyle("overflow", "hidden");
55232     this.container.addClass('x-grid-container');
55233
55234     this.id = this.container.id;
55235
55236     Roo.apply(this, config);
55237     // check and correct shorthanded configs
55238     if(this.ds){
55239         this.dataSource = this.ds;
55240         delete this.ds;
55241     }
55242     if(this.cm){
55243         this.colModel = this.cm;
55244         delete this.cm;
55245     }
55246     if(this.sm){
55247         this.selModel = this.sm;
55248         delete this.sm;
55249     }
55250
55251     if (this.selModel) {
55252         this.selModel = Roo.factory(this.selModel, Roo.grid);
55253         this.sm = this.selModel;
55254         this.sm.xmodule = this.xmodule || false;
55255     }
55256     if (typeof(this.colModel.config) == 'undefined') {
55257         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55258         this.cm = this.colModel;
55259         this.cm.xmodule = this.xmodule || false;
55260     }
55261     if (this.dataSource) {
55262         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55263         this.ds = this.dataSource;
55264         this.ds.xmodule = this.xmodule || false;
55265          
55266     }
55267     
55268     
55269     
55270     if(this.width){
55271         this.container.setWidth(this.width);
55272     }
55273
55274     if(this.height){
55275         this.container.setHeight(this.height);
55276     }
55277     /** @private */
55278         this.addEvents({
55279         // raw events
55280         /**
55281          * @event click
55282          * The raw click event for the entire grid.
55283          * @param {Roo.EventObject} e
55284          */
55285         "click" : true,
55286         /**
55287          * @event dblclick
55288          * The raw dblclick event for the entire grid.
55289          * @param {Roo.EventObject} e
55290          */
55291         "dblclick" : true,
55292         /**
55293          * @event contextmenu
55294          * The raw contextmenu event for the entire grid.
55295          * @param {Roo.EventObject} e
55296          */
55297         "contextmenu" : true,
55298         /**
55299          * @event mousedown
55300          * The raw mousedown event for the entire grid.
55301          * @param {Roo.EventObject} e
55302          */
55303         "mousedown" : true,
55304         /**
55305          * @event mouseup
55306          * The raw mouseup event for the entire grid.
55307          * @param {Roo.EventObject} e
55308          */
55309         "mouseup" : true,
55310         /**
55311          * @event mouseover
55312          * The raw mouseover event for the entire grid.
55313          * @param {Roo.EventObject} e
55314          */
55315         "mouseover" : true,
55316         /**
55317          * @event mouseout
55318          * The raw mouseout event for the entire grid.
55319          * @param {Roo.EventObject} e
55320          */
55321         "mouseout" : true,
55322         /**
55323          * @event keypress
55324          * The raw keypress event for the entire grid.
55325          * @param {Roo.EventObject} e
55326          */
55327         "keypress" : true,
55328         /**
55329          * @event keydown
55330          * The raw keydown event for the entire grid.
55331          * @param {Roo.EventObject} e
55332          */
55333         "keydown" : true,
55334
55335         // custom events
55336
55337         /**
55338          * @event cellclick
55339          * Fires when a cell is clicked
55340          * @param {Grid} this
55341          * @param {Number} rowIndex
55342          * @param {Number} columnIndex
55343          * @param {Roo.EventObject} e
55344          */
55345         "cellclick" : true,
55346         /**
55347          * @event celldblclick
55348          * Fires when a cell is double clicked
55349          * @param {Grid} this
55350          * @param {Number} rowIndex
55351          * @param {Number} columnIndex
55352          * @param {Roo.EventObject} e
55353          */
55354         "celldblclick" : true,
55355         /**
55356          * @event rowclick
55357          * Fires when a row is clicked
55358          * @param {Grid} this
55359          * @param {Number} rowIndex
55360          * @param {Roo.EventObject} e
55361          */
55362         "rowclick" : true,
55363         /**
55364          * @event rowdblclick
55365          * Fires when a row is double clicked
55366          * @param {Grid} this
55367          * @param {Number} rowIndex
55368          * @param {Roo.EventObject} e
55369          */
55370         "rowdblclick" : true,
55371         /**
55372          * @event headerclick
55373          * Fires when a header is clicked
55374          * @param {Grid} this
55375          * @param {Number} columnIndex
55376          * @param {Roo.EventObject} e
55377          */
55378         "headerclick" : true,
55379         /**
55380          * @event headerdblclick
55381          * Fires when a header cell is double clicked
55382          * @param {Grid} this
55383          * @param {Number} columnIndex
55384          * @param {Roo.EventObject} e
55385          */
55386         "headerdblclick" : true,
55387         /**
55388          * @event rowcontextmenu
55389          * Fires when a row is right clicked
55390          * @param {Grid} this
55391          * @param {Number} rowIndex
55392          * @param {Roo.EventObject} e
55393          */
55394         "rowcontextmenu" : true,
55395         /**
55396          * @event cellcontextmenu
55397          * Fires when a cell is right clicked
55398          * @param {Grid} this
55399          * @param {Number} rowIndex
55400          * @param {Number} cellIndex
55401          * @param {Roo.EventObject} e
55402          */
55403          "cellcontextmenu" : true,
55404         /**
55405          * @event headercontextmenu
55406          * Fires when a header is right clicked
55407          * @param {Grid} this
55408          * @param {Number} columnIndex
55409          * @param {Roo.EventObject} e
55410          */
55411         "headercontextmenu" : true,
55412         /**
55413          * @event bodyscroll
55414          * Fires when the body element is scrolled
55415          * @param {Number} scrollLeft
55416          * @param {Number} scrollTop
55417          */
55418         "bodyscroll" : true,
55419         /**
55420          * @event columnresize
55421          * Fires when the user resizes a column
55422          * @param {Number} columnIndex
55423          * @param {Number} newSize
55424          */
55425         "columnresize" : true,
55426         /**
55427          * @event columnmove
55428          * Fires when the user moves a column
55429          * @param {Number} oldIndex
55430          * @param {Number} newIndex
55431          */
55432         "columnmove" : true,
55433         /**
55434          * @event startdrag
55435          * Fires when row(s) start being dragged
55436          * @param {Grid} this
55437          * @param {Roo.GridDD} dd The drag drop object
55438          * @param {event} e The raw browser event
55439          */
55440         "startdrag" : true,
55441         /**
55442          * @event enddrag
55443          * Fires when a drag operation is complete
55444          * @param {Grid} this
55445          * @param {Roo.GridDD} dd The drag drop object
55446          * @param {event} e The raw browser event
55447          */
55448         "enddrag" : true,
55449         /**
55450          * @event dragdrop
55451          * Fires when dragged row(s) are dropped on a valid DD target
55452          * @param {Grid} this
55453          * @param {Roo.GridDD} dd The drag drop object
55454          * @param {String} targetId The target drag drop object
55455          * @param {event} e The raw browser event
55456          */
55457         "dragdrop" : true,
55458         /**
55459          * @event dragover
55460          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55461          * @param {Grid} this
55462          * @param {Roo.GridDD} dd The drag drop object
55463          * @param {String} targetId The target drag drop object
55464          * @param {event} e The raw browser event
55465          */
55466         "dragover" : true,
55467         /**
55468          * @event dragenter
55469          *  Fires when the dragged row(s) first cross another DD target while being dragged
55470          * @param {Grid} this
55471          * @param {Roo.GridDD} dd The drag drop object
55472          * @param {String} targetId The target drag drop object
55473          * @param {event} e The raw browser event
55474          */
55475         "dragenter" : true,
55476         /**
55477          * @event dragout
55478          * Fires when the dragged row(s) leave another DD target while being dragged
55479          * @param {Grid} this
55480          * @param {Roo.GridDD} dd The drag drop object
55481          * @param {String} targetId The target drag drop object
55482          * @param {event} e The raw browser event
55483          */
55484         "dragout" : true,
55485         /**
55486          * @event rowclass
55487          * Fires when a row is rendered, so you can change add a style to it.
55488          * @param {GridView} gridview   The grid view
55489          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55490          */
55491         'rowclass' : true,
55492
55493         /**
55494          * @event render
55495          * Fires when the grid is rendered
55496          * @param {Grid} grid
55497          */
55498         'render' : true
55499     });
55500
55501     Roo.grid.Grid.superclass.constructor.call(this);
55502 };
55503 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55504     
55505     /**
55506      * @cfg {String} ddGroup - drag drop group.
55507      */
55508       /**
55509      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55510      */
55511
55512     /**
55513      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55514      */
55515     minColumnWidth : 25,
55516
55517     /**
55518      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55519      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55520      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55521      */
55522     autoSizeColumns : false,
55523
55524     /**
55525      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55526      */
55527     autoSizeHeaders : true,
55528
55529     /**
55530      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55531      */
55532     monitorWindowResize : true,
55533
55534     /**
55535      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55536      * rows measured to get a columns size. Default is 0 (all rows).
55537      */
55538     maxRowsToMeasure : 0,
55539
55540     /**
55541      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55542      */
55543     trackMouseOver : true,
55544
55545     /**
55546     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55547     */
55548       /**
55549     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55550     */
55551     
55552     /**
55553     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55554     */
55555     enableDragDrop : false,
55556     
55557     /**
55558     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55559     */
55560     enableColumnMove : true,
55561     
55562     /**
55563     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55564     */
55565     enableColumnHide : true,
55566     
55567     /**
55568     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55569     */
55570     enableRowHeightSync : false,
55571     
55572     /**
55573     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55574     */
55575     stripeRows : true,
55576     
55577     /**
55578     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55579     */
55580     autoHeight : false,
55581
55582     /**
55583      * @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.
55584      */
55585     autoExpandColumn : false,
55586
55587     /**
55588     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55589     * Default is 50.
55590     */
55591     autoExpandMin : 50,
55592
55593     /**
55594     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55595     */
55596     autoExpandMax : 1000,
55597
55598     /**
55599     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55600     */
55601     view : null,
55602
55603     /**
55604     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55605     */
55606     loadMask : false,
55607     /**
55608     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55609     */
55610     dropTarget: false,
55611     
55612    
55613     
55614     // private
55615     rendered : false,
55616
55617     /**
55618     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55619     * of a fixed width. Default is false.
55620     */
55621     /**
55622     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55623     */
55624     
55625     
55626     /**
55627     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55628     * %0 is replaced with the number of selected rows.
55629     */
55630     ddText : "{0} selected row{1}",
55631     
55632     
55633     /**
55634      * Called once after all setup has been completed and the grid is ready to be rendered.
55635      * @return {Roo.grid.Grid} this
55636      */
55637     render : function()
55638     {
55639         var c = this.container;
55640         // try to detect autoHeight/width mode
55641         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55642             this.autoHeight = true;
55643         }
55644         var view = this.getView();
55645         view.init(this);
55646
55647         c.on("click", this.onClick, this);
55648         c.on("dblclick", this.onDblClick, this);
55649         c.on("contextmenu", this.onContextMenu, this);
55650         c.on("keydown", this.onKeyDown, this);
55651         if (Roo.isTouch) {
55652             c.on("touchstart", this.onTouchStart, this);
55653         }
55654
55655         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55656
55657         this.getSelectionModel().init(this);
55658
55659         view.render();
55660
55661         if(this.loadMask){
55662             this.loadMask = new Roo.LoadMask(this.container,
55663                     Roo.apply({store:this.dataSource}, this.loadMask));
55664         }
55665         
55666         
55667         if (this.toolbar && this.toolbar.xtype) {
55668             this.toolbar.container = this.getView().getHeaderPanel(true);
55669             this.toolbar = new Roo.Toolbar(this.toolbar);
55670         }
55671         if (this.footer && this.footer.xtype) {
55672             this.footer.dataSource = this.getDataSource();
55673             this.footer.container = this.getView().getFooterPanel(true);
55674             this.footer = Roo.factory(this.footer, Roo);
55675         }
55676         if (this.dropTarget && this.dropTarget.xtype) {
55677             delete this.dropTarget.xtype;
55678             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55679         }
55680         
55681         
55682         this.rendered = true;
55683         this.fireEvent('render', this);
55684         return this;
55685     },
55686
55687     /**
55688      * Reconfigures the grid to use a different Store and Column Model.
55689      * The View will be bound to the new objects and refreshed.
55690      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55691      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55692      */
55693     reconfigure : function(dataSource, colModel){
55694         if(this.loadMask){
55695             this.loadMask.destroy();
55696             this.loadMask = new Roo.LoadMask(this.container,
55697                     Roo.apply({store:dataSource}, this.loadMask));
55698         }
55699         this.view.bind(dataSource, colModel);
55700         this.dataSource = dataSource;
55701         this.colModel = colModel;
55702         this.view.refresh(true);
55703     },
55704     /**
55705      * addColumns
55706      * Add's a column, default at the end..
55707      
55708      * @param {int} position to add (default end)
55709      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55710      */
55711     addColumns : function(pos, ar)
55712     {
55713         
55714         for (var i =0;i< ar.length;i++) {
55715             var cfg = ar[i];
55716             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55717             this.cm.lookup[cfg.id] = cfg;
55718         }
55719         
55720         
55721         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55722             pos = this.cm.config.length; //this.cm.config.push(cfg);
55723         } 
55724         pos = Math.max(0,pos);
55725         ar.unshift(0);
55726         ar.unshift(pos);
55727         this.cm.config.splice.apply(this.cm.config, ar);
55728         
55729         
55730         
55731         this.view.generateRules(this.cm);
55732         this.view.refresh(true);
55733         
55734     },
55735     
55736     
55737     
55738     
55739     // private
55740     onKeyDown : function(e){
55741         this.fireEvent("keydown", e);
55742     },
55743
55744     /**
55745      * Destroy this grid.
55746      * @param {Boolean} removeEl True to remove the element
55747      */
55748     destroy : function(removeEl, keepListeners){
55749         if(this.loadMask){
55750             this.loadMask.destroy();
55751         }
55752         var c = this.container;
55753         c.removeAllListeners();
55754         this.view.destroy();
55755         this.colModel.purgeListeners();
55756         if(!keepListeners){
55757             this.purgeListeners();
55758         }
55759         c.update("");
55760         if(removeEl === true){
55761             c.remove();
55762         }
55763     },
55764
55765     // private
55766     processEvent : function(name, e){
55767         // does this fire select???
55768         //Roo.log('grid:processEvent '  + name);
55769         
55770         if (name != 'touchstart' ) {
55771             this.fireEvent(name, e);    
55772         }
55773         
55774         var t = e.getTarget();
55775         var v = this.view;
55776         var header = v.findHeaderIndex(t);
55777         if(header !== false){
55778             var ename = name == 'touchstart' ? 'click' : name;
55779              
55780             this.fireEvent("header" + ename, this, header, e);
55781         }else{
55782             var row = v.findRowIndex(t);
55783             var cell = v.findCellIndex(t);
55784             if (name == 'touchstart') {
55785                 // first touch is always a click.
55786                 // hopefull this happens after selection is updated.?
55787                 name = false;
55788                 
55789                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55790                     var cs = this.selModel.getSelectedCell();
55791                     if (row == cs[0] && cell == cs[1]){
55792                         name = 'dblclick';
55793                     }
55794                 }
55795                 if (typeof(this.selModel.getSelections) != 'undefined') {
55796                     var cs = this.selModel.getSelections();
55797                     var ds = this.dataSource;
55798                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55799                         name = 'dblclick';
55800                     }
55801                 }
55802                 if (!name) {
55803                     return;
55804                 }
55805             }
55806             
55807             
55808             if(row !== false){
55809                 this.fireEvent("row" + name, this, row, e);
55810                 if(cell !== false){
55811                     this.fireEvent("cell" + name, this, row, cell, e);
55812                 }
55813             }
55814         }
55815     },
55816
55817     // private
55818     onClick : function(e){
55819         this.processEvent("click", e);
55820     },
55821    // private
55822     onTouchStart : function(e){
55823         this.processEvent("touchstart", e);
55824     },
55825
55826     // private
55827     onContextMenu : function(e, t){
55828         this.processEvent("contextmenu", e);
55829     },
55830
55831     // private
55832     onDblClick : function(e){
55833         this.processEvent("dblclick", e);
55834     },
55835
55836     // private
55837     walkCells : function(row, col, step, fn, scope){
55838         var cm = this.colModel, clen = cm.getColumnCount();
55839         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55840         if(step < 0){
55841             if(col < 0){
55842                 row--;
55843                 first = false;
55844             }
55845             while(row >= 0){
55846                 if(!first){
55847                     col = clen-1;
55848                 }
55849                 first = false;
55850                 while(col >= 0){
55851                     if(fn.call(scope || this, row, col, cm) === true){
55852                         return [row, col];
55853                     }
55854                     col--;
55855                 }
55856                 row--;
55857             }
55858         } else {
55859             if(col >= clen){
55860                 row++;
55861                 first = false;
55862             }
55863             while(row < rlen){
55864                 if(!first){
55865                     col = 0;
55866                 }
55867                 first = false;
55868                 while(col < clen){
55869                     if(fn.call(scope || this, row, col, cm) === true){
55870                         return [row, col];
55871                     }
55872                     col++;
55873                 }
55874                 row++;
55875             }
55876         }
55877         return null;
55878     },
55879
55880     // private
55881     getSelections : function(){
55882         return this.selModel.getSelections();
55883     },
55884
55885     /**
55886      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55887      * but if manual update is required this method will initiate it.
55888      */
55889     autoSize : function(){
55890         if(this.rendered){
55891             this.view.layout();
55892             if(this.view.adjustForScroll){
55893                 this.view.adjustForScroll();
55894             }
55895         }
55896     },
55897
55898     /**
55899      * Returns the grid's underlying element.
55900      * @return {Element} The element
55901      */
55902     getGridEl : function(){
55903         return this.container;
55904     },
55905
55906     // private for compatibility, overridden by editor grid
55907     stopEditing : function(){},
55908
55909     /**
55910      * Returns the grid's SelectionModel.
55911      * @return {SelectionModel}
55912      */
55913     getSelectionModel : function(){
55914         if(!this.selModel){
55915             this.selModel = new Roo.grid.RowSelectionModel();
55916         }
55917         return this.selModel;
55918     },
55919
55920     /**
55921      * Returns the grid's DataSource.
55922      * @return {DataSource}
55923      */
55924     getDataSource : function(){
55925         return this.dataSource;
55926     },
55927
55928     /**
55929      * Returns the grid's ColumnModel.
55930      * @return {ColumnModel}
55931      */
55932     getColumnModel : function(){
55933         return this.colModel;
55934     },
55935
55936     /**
55937      * Returns the grid's GridView object.
55938      * @return {GridView}
55939      */
55940     getView : function(){
55941         if(!this.view){
55942             this.view = new Roo.grid.GridView(this.viewConfig);
55943             this.relayEvents(this.view, [
55944                 "beforerowremoved", "beforerowsinserted",
55945                 "beforerefresh", "rowremoved",
55946                 "rowsinserted", "rowupdated" ,"refresh"
55947             ]);
55948         }
55949         return this.view;
55950     },
55951     /**
55952      * Called to get grid's drag proxy text, by default returns this.ddText.
55953      * Override this to put something different in the dragged text.
55954      * @return {String}
55955      */
55956     getDragDropText : function(){
55957         var count = this.selModel.getCount();
55958         return String.format(this.ddText, count, count == 1 ? '' : 's');
55959     }
55960 });
55961 /*
55962  * Based on:
55963  * Ext JS Library 1.1.1
55964  * Copyright(c) 2006-2007, Ext JS, LLC.
55965  *
55966  * Originally Released Under LGPL - original licence link has changed is not relivant.
55967  *
55968  * Fork - LGPL
55969  * <script type="text/javascript">
55970  */
55971  
55972 Roo.grid.AbstractGridView = function(){
55973         this.grid = null;
55974         
55975         this.events = {
55976             "beforerowremoved" : true,
55977             "beforerowsinserted" : true,
55978             "beforerefresh" : true,
55979             "rowremoved" : true,
55980             "rowsinserted" : true,
55981             "rowupdated" : true,
55982             "refresh" : true
55983         };
55984     Roo.grid.AbstractGridView.superclass.constructor.call(this);
55985 };
55986
55987 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
55988     rowClass : "x-grid-row",
55989     cellClass : "x-grid-cell",
55990     tdClass : "x-grid-td",
55991     hdClass : "x-grid-hd",
55992     splitClass : "x-grid-hd-split",
55993     
55994     init: function(grid){
55995         this.grid = grid;
55996                 var cid = this.grid.getGridEl().id;
55997         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
55998         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
55999         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56000         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56001         },
56002         
56003     getColumnRenderers : function(){
56004         var renderers = [];
56005         var cm = this.grid.colModel;
56006         var colCount = cm.getColumnCount();
56007         for(var i = 0; i < colCount; i++){
56008             renderers[i] = cm.getRenderer(i);
56009         }
56010         return renderers;
56011     },
56012     
56013     getColumnIds : function(){
56014         var ids = [];
56015         var cm = this.grid.colModel;
56016         var colCount = cm.getColumnCount();
56017         for(var i = 0; i < colCount; i++){
56018             ids[i] = cm.getColumnId(i);
56019         }
56020         return ids;
56021     },
56022     
56023     getDataIndexes : function(){
56024         if(!this.indexMap){
56025             this.indexMap = this.buildIndexMap();
56026         }
56027         return this.indexMap.colToData;
56028     },
56029     
56030     getColumnIndexByDataIndex : function(dataIndex){
56031         if(!this.indexMap){
56032             this.indexMap = this.buildIndexMap();
56033         }
56034         return this.indexMap.dataToCol[dataIndex];
56035     },
56036     
56037     /**
56038      * Set a css style for a column dynamically. 
56039      * @param {Number} colIndex The index of the column
56040      * @param {String} name The css property name
56041      * @param {String} value The css value
56042      */
56043     setCSSStyle : function(colIndex, name, value){
56044         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56045         Roo.util.CSS.updateRule(selector, name, value);
56046     },
56047     
56048     generateRules : function(cm){
56049         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56050         Roo.util.CSS.removeStyleSheet(rulesId);
56051         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56052             var cid = cm.getColumnId(i);
56053             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56054                          this.tdSelector, cid, " {\n}\n",
56055                          this.hdSelector, cid, " {\n}\n",
56056                          this.splitSelector, cid, " {\n}\n");
56057         }
56058         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56059     }
56060 });/*
56061  * Based on:
56062  * Ext JS Library 1.1.1
56063  * Copyright(c) 2006-2007, Ext JS, LLC.
56064  *
56065  * Originally Released Under LGPL - original licence link has changed is not relivant.
56066  *
56067  * Fork - LGPL
56068  * <script type="text/javascript">
56069  */
56070
56071 // private
56072 // This is a support class used internally by the Grid components
56073 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56074     this.grid = grid;
56075     this.view = grid.getView();
56076     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56077     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56078     if(hd2){
56079         this.setHandleElId(Roo.id(hd));
56080         this.setOuterHandleElId(Roo.id(hd2));
56081     }
56082     this.scroll = false;
56083 };
56084 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56085     maxDragWidth: 120,
56086     getDragData : function(e){
56087         var t = Roo.lib.Event.getTarget(e);
56088         var h = this.view.findHeaderCell(t);
56089         if(h){
56090             return {ddel: h.firstChild, header:h};
56091         }
56092         return false;
56093     },
56094
56095     onInitDrag : function(e){
56096         this.view.headersDisabled = true;
56097         var clone = this.dragData.ddel.cloneNode(true);
56098         clone.id = Roo.id();
56099         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56100         this.proxy.update(clone);
56101         return true;
56102     },
56103
56104     afterValidDrop : function(){
56105         var v = this.view;
56106         setTimeout(function(){
56107             v.headersDisabled = false;
56108         }, 50);
56109     },
56110
56111     afterInvalidDrop : function(){
56112         var v = this.view;
56113         setTimeout(function(){
56114             v.headersDisabled = false;
56115         }, 50);
56116     }
56117 });
56118 /*
56119  * Based on:
56120  * Ext JS Library 1.1.1
56121  * Copyright(c) 2006-2007, Ext JS, LLC.
56122  *
56123  * Originally Released Under LGPL - original licence link has changed is not relivant.
56124  *
56125  * Fork - LGPL
56126  * <script type="text/javascript">
56127  */
56128 // private
56129 // This is a support class used internally by the Grid components
56130 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56131     this.grid = grid;
56132     this.view = grid.getView();
56133     // split the proxies so they don't interfere with mouse events
56134     this.proxyTop = Roo.DomHelper.append(document.body, {
56135         cls:"col-move-top", html:"&#160;"
56136     }, true);
56137     this.proxyBottom = Roo.DomHelper.append(document.body, {
56138         cls:"col-move-bottom", html:"&#160;"
56139     }, true);
56140     this.proxyTop.hide = this.proxyBottom.hide = function(){
56141         this.setLeftTop(-100,-100);
56142         this.setStyle("visibility", "hidden");
56143     };
56144     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56145     // temporarily disabled
56146     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56147     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56148 };
56149 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56150     proxyOffsets : [-4, -9],
56151     fly: Roo.Element.fly,
56152
56153     getTargetFromEvent : function(e){
56154         var t = Roo.lib.Event.getTarget(e);
56155         var cindex = this.view.findCellIndex(t);
56156         if(cindex !== false){
56157             return this.view.getHeaderCell(cindex);
56158         }
56159         return null;
56160     },
56161
56162     nextVisible : function(h){
56163         var v = this.view, cm = this.grid.colModel;
56164         h = h.nextSibling;
56165         while(h){
56166             if(!cm.isHidden(v.getCellIndex(h))){
56167                 return h;
56168             }
56169             h = h.nextSibling;
56170         }
56171         return null;
56172     },
56173
56174     prevVisible : function(h){
56175         var v = this.view, cm = this.grid.colModel;
56176         h = h.prevSibling;
56177         while(h){
56178             if(!cm.isHidden(v.getCellIndex(h))){
56179                 return h;
56180             }
56181             h = h.prevSibling;
56182         }
56183         return null;
56184     },
56185
56186     positionIndicator : function(h, n, e){
56187         var x = Roo.lib.Event.getPageX(e);
56188         var r = Roo.lib.Dom.getRegion(n.firstChild);
56189         var px, pt, py = r.top + this.proxyOffsets[1];
56190         if((r.right - x) <= (r.right-r.left)/2){
56191             px = r.right+this.view.borderWidth;
56192             pt = "after";
56193         }else{
56194             px = r.left;
56195             pt = "before";
56196         }
56197         var oldIndex = this.view.getCellIndex(h);
56198         var newIndex = this.view.getCellIndex(n);
56199
56200         if(this.grid.colModel.isFixed(newIndex)){
56201             return false;
56202         }
56203
56204         var locked = this.grid.colModel.isLocked(newIndex);
56205
56206         if(pt == "after"){
56207             newIndex++;
56208         }
56209         if(oldIndex < newIndex){
56210             newIndex--;
56211         }
56212         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56213             return false;
56214         }
56215         px +=  this.proxyOffsets[0];
56216         this.proxyTop.setLeftTop(px, py);
56217         this.proxyTop.show();
56218         if(!this.bottomOffset){
56219             this.bottomOffset = this.view.mainHd.getHeight();
56220         }
56221         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56222         this.proxyBottom.show();
56223         return pt;
56224     },
56225
56226     onNodeEnter : function(n, dd, e, data){
56227         if(data.header != n){
56228             this.positionIndicator(data.header, n, e);
56229         }
56230     },
56231
56232     onNodeOver : function(n, dd, e, data){
56233         var result = false;
56234         if(data.header != n){
56235             result = this.positionIndicator(data.header, n, e);
56236         }
56237         if(!result){
56238             this.proxyTop.hide();
56239             this.proxyBottom.hide();
56240         }
56241         return result ? this.dropAllowed : this.dropNotAllowed;
56242     },
56243
56244     onNodeOut : function(n, dd, e, data){
56245         this.proxyTop.hide();
56246         this.proxyBottom.hide();
56247     },
56248
56249     onNodeDrop : function(n, dd, e, data){
56250         var h = data.header;
56251         if(h != n){
56252             var cm = this.grid.colModel;
56253             var x = Roo.lib.Event.getPageX(e);
56254             var r = Roo.lib.Dom.getRegion(n.firstChild);
56255             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56256             var oldIndex = this.view.getCellIndex(h);
56257             var newIndex = this.view.getCellIndex(n);
56258             var locked = cm.isLocked(newIndex);
56259             if(pt == "after"){
56260                 newIndex++;
56261             }
56262             if(oldIndex < newIndex){
56263                 newIndex--;
56264             }
56265             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56266                 return false;
56267             }
56268             cm.setLocked(oldIndex, locked, true);
56269             cm.moveColumn(oldIndex, newIndex);
56270             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56271             return true;
56272         }
56273         return false;
56274     }
56275 });
56276 /*
56277  * Based on:
56278  * Ext JS Library 1.1.1
56279  * Copyright(c) 2006-2007, Ext JS, LLC.
56280  *
56281  * Originally Released Under LGPL - original licence link has changed is not relivant.
56282  *
56283  * Fork - LGPL
56284  * <script type="text/javascript">
56285  */
56286   
56287 /**
56288  * @class Roo.grid.GridView
56289  * @extends Roo.util.Observable
56290  *
56291  * @constructor
56292  * @param {Object} config
56293  */
56294 Roo.grid.GridView = function(config){
56295     Roo.grid.GridView.superclass.constructor.call(this);
56296     this.el = null;
56297
56298     Roo.apply(this, config);
56299 };
56300
56301 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56302
56303     unselectable :  'unselectable="on"',
56304     unselectableCls :  'x-unselectable',
56305     
56306     
56307     rowClass : "x-grid-row",
56308
56309     cellClass : "x-grid-col",
56310
56311     tdClass : "x-grid-td",
56312
56313     hdClass : "x-grid-hd",
56314
56315     splitClass : "x-grid-split",
56316
56317     sortClasses : ["sort-asc", "sort-desc"],
56318
56319     enableMoveAnim : false,
56320
56321     hlColor: "C3DAF9",
56322
56323     dh : Roo.DomHelper,
56324
56325     fly : Roo.Element.fly,
56326
56327     css : Roo.util.CSS,
56328
56329     borderWidth: 1,
56330
56331     splitOffset: 3,
56332
56333     scrollIncrement : 22,
56334
56335     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56336
56337     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56338
56339     bind : function(ds, cm){
56340         if(this.ds){
56341             this.ds.un("load", this.onLoad, this);
56342             this.ds.un("datachanged", this.onDataChange, this);
56343             this.ds.un("add", this.onAdd, this);
56344             this.ds.un("remove", this.onRemove, this);
56345             this.ds.un("update", this.onUpdate, this);
56346             this.ds.un("clear", this.onClear, this);
56347         }
56348         if(ds){
56349             ds.on("load", this.onLoad, this);
56350             ds.on("datachanged", this.onDataChange, this);
56351             ds.on("add", this.onAdd, this);
56352             ds.on("remove", this.onRemove, this);
56353             ds.on("update", this.onUpdate, this);
56354             ds.on("clear", this.onClear, this);
56355         }
56356         this.ds = ds;
56357
56358         if(this.cm){
56359             this.cm.un("widthchange", this.onColWidthChange, this);
56360             this.cm.un("headerchange", this.onHeaderChange, this);
56361             this.cm.un("hiddenchange", this.onHiddenChange, this);
56362             this.cm.un("columnmoved", this.onColumnMove, this);
56363             this.cm.un("columnlockchange", this.onColumnLock, this);
56364         }
56365         if(cm){
56366             this.generateRules(cm);
56367             cm.on("widthchange", this.onColWidthChange, this);
56368             cm.on("headerchange", this.onHeaderChange, this);
56369             cm.on("hiddenchange", this.onHiddenChange, this);
56370             cm.on("columnmoved", this.onColumnMove, this);
56371             cm.on("columnlockchange", this.onColumnLock, this);
56372         }
56373         this.cm = cm;
56374     },
56375
56376     init: function(grid){
56377         Roo.grid.GridView.superclass.init.call(this, grid);
56378
56379         this.bind(grid.dataSource, grid.colModel);
56380
56381         grid.on("headerclick", this.handleHeaderClick, this);
56382
56383         if(grid.trackMouseOver){
56384             grid.on("mouseover", this.onRowOver, this);
56385             grid.on("mouseout", this.onRowOut, this);
56386         }
56387         grid.cancelTextSelection = function(){};
56388         this.gridId = grid.id;
56389
56390         var tpls = this.templates || {};
56391
56392         if(!tpls.master){
56393             tpls.master = new Roo.Template(
56394                '<div class="x-grid" hidefocus="true">',
56395                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56396                   '<div class="x-grid-topbar"></div>',
56397                   '<div class="x-grid-scroller"><div></div></div>',
56398                   '<div class="x-grid-locked">',
56399                       '<div class="x-grid-header">{lockedHeader}</div>',
56400                       '<div class="x-grid-body">{lockedBody}</div>',
56401                   "</div>",
56402                   '<div class="x-grid-viewport">',
56403                       '<div class="x-grid-header">{header}</div>',
56404                       '<div class="x-grid-body">{body}</div>',
56405                   "</div>",
56406                   '<div class="x-grid-bottombar"></div>',
56407                  
56408                   '<div class="x-grid-resize-proxy">&#160;</div>',
56409                "</div>"
56410             );
56411             tpls.master.disableformats = true;
56412         }
56413
56414         if(!tpls.header){
56415             tpls.header = new Roo.Template(
56416                '<table border="0" cellspacing="0" cellpadding="0">',
56417                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56418                "</table>{splits}"
56419             );
56420             tpls.header.disableformats = true;
56421         }
56422         tpls.header.compile();
56423
56424         if(!tpls.hcell){
56425             tpls.hcell = new Roo.Template(
56426                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56427                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56428                 "</div></td>"
56429              );
56430              tpls.hcell.disableFormats = true;
56431         }
56432         tpls.hcell.compile();
56433
56434         if(!tpls.hsplit){
56435             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56436                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56437             tpls.hsplit.disableFormats = true;
56438         }
56439         tpls.hsplit.compile();
56440
56441         if(!tpls.body){
56442             tpls.body = new Roo.Template(
56443                '<table border="0" cellspacing="0" cellpadding="0">',
56444                "<tbody>{rows}</tbody>",
56445                "</table>"
56446             );
56447             tpls.body.disableFormats = true;
56448         }
56449         tpls.body.compile();
56450
56451         if(!tpls.row){
56452             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56453             tpls.row.disableFormats = true;
56454         }
56455         tpls.row.compile();
56456
56457         if(!tpls.cell){
56458             tpls.cell = new Roo.Template(
56459                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56460                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56461                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56462                 "</td>"
56463             );
56464             tpls.cell.disableFormats = true;
56465         }
56466         tpls.cell.compile();
56467
56468         this.templates = tpls;
56469     },
56470
56471     // remap these for backwards compat
56472     onColWidthChange : function(){
56473         this.updateColumns.apply(this, arguments);
56474     },
56475     onHeaderChange : function(){
56476         this.updateHeaders.apply(this, arguments);
56477     }, 
56478     onHiddenChange : function(){
56479         this.handleHiddenChange.apply(this, arguments);
56480     },
56481     onColumnMove : function(){
56482         this.handleColumnMove.apply(this, arguments);
56483     },
56484     onColumnLock : function(){
56485         this.handleLockChange.apply(this, arguments);
56486     },
56487
56488     onDataChange : function(){
56489         this.refresh();
56490         this.updateHeaderSortState();
56491     },
56492
56493     onClear : function(){
56494         this.refresh();
56495     },
56496
56497     onUpdate : function(ds, record){
56498         this.refreshRow(record);
56499     },
56500
56501     refreshRow : function(record){
56502         var ds = this.ds, index;
56503         if(typeof record == 'number'){
56504             index = record;
56505             record = ds.getAt(index);
56506         }else{
56507             index = ds.indexOf(record);
56508         }
56509         this.insertRows(ds, index, index, true);
56510         this.onRemove(ds, record, index+1, true);
56511         this.syncRowHeights(index, index);
56512         this.layout();
56513         this.fireEvent("rowupdated", this, index, record);
56514     },
56515
56516     onAdd : function(ds, records, index){
56517         this.insertRows(ds, index, index + (records.length-1));
56518     },
56519
56520     onRemove : function(ds, record, index, isUpdate){
56521         if(isUpdate !== true){
56522             this.fireEvent("beforerowremoved", this, index, record);
56523         }
56524         var bt = this.getBodyTable(), lt = this.getLockedTable();
56525         if(bt.rows[index]){
56526             bt.firstChild.removeChild(bt.rows[index]);
56527         }
56528         if(lt.rows[index]){
56529             lt.firstChild.removeChild(lt.rows[index]);
56530         }
56531         if(isUpdate !== true){
56532             this.stripeRows(index);
56533             this.syncRowHeights(index, index);
56534             this.layout();
56535             this.fireEvent("rowremoved", this, index, record);
56536         }
56537     },
56538
56539     onLoad : function(){
56540         this.scrollToTop();
56541     },
56542
56543     /**
56544      * Scrolls the grid to the top
56545      */
56546     scrollToTop : function(){
56547         if(this.scroller){
56548             this.scroller.dom.scrollTop = 0;
56549             this.syncScroll();
56550         }
56551     },
56552
56553     /**
56554      * Gets a panel in the header of the grid that can be used for toolbars etc.
56555      * After modifying the contents of this panel a call to grid.autoSize() may be
56556      * required to register any changes in size.
56557      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56558      * @return Roo.Element
56559      */
56560     getHeaderPanel : function(doShow){
56561         if(doShow){
56562             this.headerPanel.show();
56563         }
56564         return this.headerPanel;
56565     },
56566
56567     /**
56568      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56569      * After modifying the contents of this panel a call to grid.autoSize() may be
56570      * required to register any changes in size.
56571      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56572      * @return Roo.Element
56573      */
56574     getFooterPanel : function(doShow){
56575         if(doShow){
56576             this.footerPanel.show();
56577         }
56578         return this.footerPanel;
56579     },
56580
56581     initElements : function(){
56582         var E = Roo.Element;
56583         var el = this.grid.getGridEl().dom.firstChild;
56584         var cs = el.childNodes;
56585
56586         this.el = new E(el);
56587         
56588          this.focusEl = new E(el.firstChild);
56589         this.focusEl.swallowEvent("click", true);
56590         
56591         this.headerPanel = new E(cs[1]);
56592         this.headerPanel.enableDisplayMode("block");
56593
56594         this.scroller = new E(cs[2]);
56595         this.scrollSizer = new E(this.scroller.dom.firstChild);
56596
56597         this.lockedWrap = new E(cs[3]);
56598         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56599         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56600
56601         this.mainWrap = new E(cs[4]);
56602         this.mainHd = new E(this.mainWrap.dom.firstChild);
56603         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56604
56605         this.footerPanel = new E(cs[5]);
56606         this.footerPanel.enableDisplayMode("block");
56607
56608         this.resizeProxy = new E(cs[6]);
56609
56610         this.headerSelector = String.format(
56611            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56612            this.lockedHd.id, this.mainHd.id
56613         );
56614
56615         this.splitterSelector = String.format(
56616            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56617            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56618         );
56619     },
56620     idToCssName : function(s)
56621     {
56622         return s.replace(/[^a-z0-9]+/ig, '-');
56623     },
56624
56625     getHeaderCell : function(index){
56626         return Roo.DomQuery.select(this.headerSelector)[index];
56627     },
56628
56629     getHeaderCellMeasure : function(index){
56630         return this.getHeaderCell(index).firstChild;
56631     },
56632
56633     getHeaderCellText : function(index){
56634         return this.getHeaderCell(index).firstChild.firstChild;
56635     },
56636
56637     getLockedTable : function(){
56638         return this.lockedBody.dom.firstChild;
56639     },
56640
56641     getBodyTable : function(){
56642         return this.mainBody.dom.firstChild;
56643     },
56644
56645     getLockedRow : function(index){
56646         return this.getLockedTable().rows[index];
56647     },
56648
56649     getRow : function(index){
56650         return this.getBodyTable().rows[index];
56651     },
56652
56653     getRowComposite : function(index){
56654         if(!this.rowEl){
56655             this.rowEl = new Roo.CompositeElementLite();
56656         }
56657         var els = [], lrow, mrow;
56658         if(lrow = this.getLockedRow(index)){
56659             els.push(lrow);
56660         }
56661         if(mrow = this.getRow(index)){
56662             els.push(mrow);
56663         }
56664         this.rowEl.elements = els;
56665         return this.rowEl;
56666     },
56667     /**
56668      * Gets the 'td' of the cell
56669      * 
56670      * @param {Integer} rowIndex row to select
56671      * @param {Integer} colIndex column to select
56672      * 
56673      * @return {Object} 
56674      */
56675     getCell : function(rowIndex, colIndex){
56676         var locked = this.cm.getLockedCount();
56677         var source;
56678         if(colIndex < locked){
56679             source = this.lockedBody.dom.firstChild;
56680         }else{
56681             source = this.mainBody.dom.firstChild;
56682             colIndex -= locked;
56683         }
56684         return source.rows[rowIndex].childNodes[colIndex];
56685     },
56686
56687     getCellText : function(rowIndex, colIndex){
56688         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56689     },
56690
56691     getCellBox : function(cell){
56692         var b = this.fly(cell).getBox();
56693         if(Roo.isOpera){ // opera fails to report the Y
56694             b.y = cell.offsetTop + this.mainBody.getY();
56695         }
56696         return b;
56697     },
56698
56699     getCellIndex : function(cell){
56700         var id = String(cell.className).match(this.cellRE);
56701         if(id){
56702             return parseInt(id[1], 10);
56703         }
56704         return 0;
56705     },
56706
56707     findHeaderIndex : function(n){
56708         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56709         return r ? this.getCellIndex(r) : false;
56710     },
56711
56712     findHeaderCell : function(n){
56713         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56714         return r ? r : false;
56715     },
56716
56717     findRowIndex : function(n){
56718         if(!n){
56719             return false;
56720         }
56721         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56722         return r ? r.rowIndex : false;
56723     },
56724
56725     findCellIndex : function(node){
56726         var stop = this.el.dom;
56727         while(node && node != stop){
56728             if(this.findRE.test(node.className)){
56729                 return this.getCellIndex(node);
56730             }
56731             node = node.parentNode;
56732         }
56733         return false;
56734     },
56735
56736     getColumnId : function(index){
56737         return this.cm.getColumnId(index);
56738     },
56739
56740     getSplitters : function()
56741     {
56742         if(this.splitterSelector){
56743            return Roo.DomQuery.select(this.splitterSelector);
56744         }else{
56745             return null;
56746       }
56747     },
56748
56749     getSplitter : function(index){
56750         return this.getSplitters()[index];
56751     },
56752
56753     onRowOver : function(e, t){
56754         var row;
56755         if((row = this.findRowIndex(t)) !== false){
56756             this.getRowComposite(row).addClass("x-grid-row-over");
56757         }
56758     },
56759
56760     onRowOut : function(e, t){
56761         var row;
56762         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56763             this.getRowComposite(row).removeClass("x-grid-row-over");
56764         }
56765     },
56766
56767     renderHeaders : function(){
56768         var cm = this.cm;
56769         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56770         var cb = [], lb = [], sb = [], lsb = [], p = {};
56771         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56772             p.cellId = "x-grid-hd-0-" + i;
56773             p.splitId = "x-grid-csplit-0-" + i;
56774             p.id = cm.getColumnId(i);
56775             p.value = cm.getColumnHeader(i) || "";
56776             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56777             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56778             if(!cm.isLocked(i)){
56779                 cb[cb.length] = ct.apply(p);
56780                 sb[sb.length] = st.apply(p);
56781             }else{
56782                 lb[lb.length] = ct.apply(p);
56783                 lsb[lsb.length] = st.apply(p);
56784             }
56785         }
56786         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56787                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56788     },
56789
56790     updateHeaders : function(){
56791         var html = this.renderHeaders();
56792         this.lockedHd.update(html[0]);
56793         this.mainHd.update(html[1]);
56794     },
56795
56796     /**
56797      * Focuses the specified row.
56798      * @param {Number} row The row index
56799      */
56800     focusRow : function(row)
56801     {
56802         //Roo.log('GridView.focusRow');
56803         var x = this.scroller.dom.scrollLeft;
56804         this.focusCell(row, 0, false);
56805         this.scroller.dom.scrollLeft = x;
56806     },
56807
56808     /**
56809      * Focuses the specified cell.
56810      * @param {Number} row The row index
56811      * @param {Number} col The column index
56812      * @param {Boolean} hscroll false to disable horizontal scrolling
56813      */
56814     focusCell : function(row, col, hscroll)
56815     {
56816         //Roo.log('GridView.focusCell');
56817         var el = this.ensureVisible(row, col, hscroll);
56818         this.focusEl.alignTo(el, "tl-tl");
56819         if(Roo.isGecko){
56820             this.focusEl.focus();
56821         }else{
56822             this.focusEl.focus.defer(1, this.focusEl);
56823         }
56824     },
56825
56826     /**
56827      * Scrolls the specified cell into view
56828      * @param {Number} row The row index
56829      * @param {Number} col The column index
56830      * @param {Boolean} hscroll false to disable horizontal scrolling
56831      */
56832     ensureVisible : function(row, col, hscroll)
56833     {
56834         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56835         //return null; //disable for testing.
56836         if(typeof row != "number"){
56837             row = row.rowIndex;
56838         }
56839         if(row < 0 && row >= this.ds.getCount()){
56840             return  null;
56841         }
56842         col = (col !== undefined ? col : 0);
56843         var cm = this.grid.colModel;
56844         while(cm.isHidden(col)){
56845             col++;
56846         }
56847
56848         var el = this.getCell(row, col);
56849         if(!el){
56850             return null;
56851         }
56852         var c = this.scroller.dom;
56853
56854         var ctop = parseInt(el.offsetTop, 10);
56855         var cleft = parseInt(el.offsetLeft, 10);
56856         var cbot = ctop + el.offsetHeight;
56857         var cright = cleft + el.offsetWidth;
56858         
56859         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56860         var stop = parseInt(c.scrollTop, 10);
56861         var sleft = parseInt(c.scrollLeft, 10);
56862         var sbot = stop + ch;
56863         var sright = sleft + c.clientWidth;
56864         /*
56865         Roo.log('GridView.ensureVisible:' +
56866                 ' ctop:' + ctop +
56867                 ' c.clientHeight:' + c.clientHeight +
56868                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56869                 ' stop:' + stop +
56870                 ' cbot:' + cbot +
56871                 ' sbot:' + sbot +
56872                 ' ch:' + ch  
56873                 );
56874         */
56875         if(ctop < stop){
56876             c.scrollTop = ctop;
56877             //Roo.log("set scrolltop to ctop DISABLE?");
56878         }else if(cbot > sbot){
56879             //Roo.log("set scrolltop to cbot-ch");
56880             c.scrollTop = cbot-ch;
56881         }
56882         
56883         if(hscroll !== false){
56884             if(cleft < sleft){
56885                 c.scrollLeft = cleft;
56886             }else if(cright > sright){
56887                 c.scrollLeft = cright-c.clientWidth;
56888             }
56889         }
56890          
56891         return el;
56892     },
56893
56894     updateColumns : function(){
56895         this.grid.stopEditing();
56896         var cm = this.grid.colModel, colIds = this.getColumnIds();
56897         //var totalWidth = cm.getTotalWidth();
56898         var pos = 0;
56899         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56900             //if(cm.isHidden(i)) continue;
56901             var w = cm.getColumnWidth(i);
56902             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56903             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56904         }
56905         this.updateSplitters();
56906     },
56907
56908     generateRules : function(cm){
56909         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56910         Roo.util.CSS.removeStyleSheet(rulesId);
56911         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56912             var cid = cm.getColumnId(i);
56913             var align = '';
56914             if(cm.config[i].align){
56915                 align = 'text-align:'+cm.config[i].align+';';
56916             }
56917             var hidden = '';
56918             if(cm.isHidden(i)){
56919                 hidden = 'display:none;';
56920             }
56921             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56922             ruleBuf.push(
56923                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56924                     this.hdSelector, cid, " {\n", align, width, "}\n",
56925                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56926                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56927         }
56928         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56929     },
56930
56931     updateSplitters : function(){
56932         var cm = this.cm, s = this.getSplitters();
56933         if(s){ // splitters not created yet
56934             var pos = 0, locked = true;
56935             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56936                 if(cm.isHidden(i)) {
56937                     continue;
56938                 }
56939                 var w = cm.getColumnWidth(i); // make sure it's a number
56940                 if(!cm.isLocked(i) && locked){
56941                     pos = 0;
56942                     locked = false;
56943                 }
56944                 pos += w;
56945                 s[i].style.left = (pos-this.splitOffset) + "px";
56946             }
56947         }
56948     },
56949
56950     handleHiddenChange : function(colModel, colIndex, hidden){
56951         if(hidden){
56952             this.hideColumn(colIndex);
56953         }else{
56954             this.unhideColumn(colIndex);
56955         }
56956     },
56957
56958     hideColumn : function(colIndex){
56959         var cid = this.getColumnId(colIndex);
56960         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
56961         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
56962         if(Roo.isSafari){
56963             this.updateHeaders();
56964         }
56965         this.updateSplitters();
56966         this.layout();
56967     },
56968
56969     unhideColumn : function(colIndex){
56970         var cid = this.getColumnId(colIndex);
56971         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
56972         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
56973
56974         if(Roo.isSafari){
56975             this.updateHeaders();
56976         }
56977         this.updateSplitters();
56978         this.layout();
56979     },
56980
56981     insertRows : function(dm, firstRow, lastRow, isUpdate){
56982         if(firstRow == 0 && lastRow == dm.getCount()-1){
56983             this.refresh();
56984         }else{
56985             if(!isUpdate){
56986                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
56987             }
56988             var s = this.getScrollState();
56989             var markup = this.renderRows(firstRow, lastRow);
56990             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
56991             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
56992             this.restoreScroll(s);
56993             if(!isUpdate){
56994                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
56995                 this.syncRowHeights(firstRow, lastRow);
56996                 this.stripeRows(firstRow);
56997                 this.layout();
56998             }
56999         }
57000     },
57001
57002     bufferRows : function(markup, target, index){
57003         var before = null, trows = target.rows, tbody = target.tBodies[0];
57004         if(index < trows.length){
57005             before = trows[index];
57006         }
57007         var b = document.createElement("div");
57008         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57009         var rows = b.firstChild.rows;
57010         for(var i = 0, len = rows.length; i < len; i++){
57011             if(before){
57012                 tbody.insertBefore(rows[0], before);
57013             }else{
57014                 tbody.appendChild(rows[0]);
57015             }
57016         }
57017         b.innerHTML = "";
57018         b = null;
57019     },
57020
57021     deleteRows : function(dm, firstRow, lastRow){
57022         if(dm.getRowCount()<1){
57023             this.fireEvent("beforerefresh", this);
57024             this.mainBody.update("");
57025             this.lockedBody.update("");
57026             this.fireEvent("refresh", this);
57027         }else{
57028             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57029             var bt = this.getBodyTable();
57030             var tbody = bt.firstChild;
57031             var rows = bt.rows;
57032             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57033                 tbody.removeChild(rows[firstRow]);
57034             }
57035             this.stripeRows(firstRow);
57036             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57037         }
57038     },
57039
57040     updateRows : function(dataSource, firstRow, lastRow){
57041         var s = this.getScrollState();
57042         this.refresh();
57043         this.restoreScroll(s);
57044     },
57045
57046     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57047         if(!noRefresh){
57048            this.refresh();
57049         }
57050         this.updateHeaderSortState();
57051     },
57052
57053     getScrollState : function(){
57054         
57055         var sb = this.scroller.dom;
57056         return {left: sb.scrollLeft, top: sb.scrollTop};
57057     },
57058
57059     stripeRows : function(startRow){
57060         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57061             return;
57062         }
57063         startRow = startRow || 0;
57064         var rows = this.getBodyTable().rows;
57065         var lrows = this.getLockedTable().rows;
57066         var cls = ' x-grid-row-alt ';
57067         for(var i = startRow, len = rows.length; i < len; i++){
57068             var row = rows[i], lrow = lrows[i];
57069             var isAlt = ((i+1) % 2 == 0);
57070             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57071             if(isAlt == hasAlt){
57072                 continue;
57073             }
57074             if(isAlt){
57075                 row.className += " x-grid-row-alt";
57076             }else{
57077                 row.className = row.className.replace("x-grid-row-alt", "");
57078             }
57079             if(lrow){
57080                 lrow.className = row.className;
57081             }
57082         }
57083     },
57084
57085     restoreScroll : function(state){
57086         //Roo.log('GridView.restoreScroll');
57087         var sb = this.scroller.dom;
57088         sb.scrollLeft = state.left;
57089         sb.scrollTop = state.top;
57090         this.syncScroll();
57091     },
57092
57093     syncScroll : function(){
57094         //Roo.log('GridView.syncScroll');
57095         var sb = this.scroller.dom;
57096         var sh = this.mainHd.dom;
57097         var bs = this.mainBody.dom;
57098         var lv = this.lockedBody.dom;
57099         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57100         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57101     },
57102
57103     handleScroll : function(e){
57104         this.syncScroll();
57105         var sb = this.scroller.dom;
57106         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57107         e.stopEvent();
57108     },
57109
57110     handleWheel : function(e){
57111         var d = e.getWheelDelta();
57112         this.scroller.dom.scrollTop -= d*22;
57113         // set this here to prevent jumpy scrolling on large tables
57114         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57115         e.stopEvent();
57116     },
57117
57118     renderRows : function(startRow, endRow){
57119         // pull in all the crap needed to render rows
57120         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57121         var colCount = cm.getColumnCount();
57122
57123         if(ds.getCount() < 1){
57124             return ["", ""];
57125         }
57126
57127         // build a map for all the columns
57128         var cs = [];
57129         for(var i = 0; i < colCount; i++){
57130             var name = cm.getDataIndex(i);
57131             cs[i] = {
57132                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57133                 renderer : cm.getRenderer(i),
57134                 id : cm.getColumnId(i),
57135                 locked : cm.isLocked(i),
57136                 has_editor : cm.isCellEditable(i)
57137             };
57138         }
57139
57140         startRow = startRow || 0;
57141         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57142
57143         // records to render
57144         var rs = ds.getRange(startRow, endRow);
57145
57146         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57147     },
57148
57149     // As much as I hate to duplicate code, this was branched because FireFox really hates
57150     // [].join("") on strings. The performance difference was substantial enough to
57151     // branch this function
57152     doRender : Roo.isGecko ?
57153             function(cs, rs, ds, startRow, colCount, stripe){
57154                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57155                 // buffers
57156                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57157                 
57158                 var hasListener = this.grid.hasListener('rowclass');
57159                 var rowcfg = {};
57160                 for(var j = 0, len = rs.length; j < len; j++){
57161                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57162                     for(var i = 0; i < colCount; i++){
57163                         c = cs[i];
57164                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57165                         p.id = c.id;
57166                         p.css = p.attr = "";
57167                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57168                         if(p.value == undefined || p.value === "") {
57169                             p.value = "&#160;";
57170                         }
57171                         if(c.has_editor){
57172                             p.css += ' x-grid-editable-cell';
57173                         }
57174                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57175                             p.css +=  ' x-grid-dirty-cell';
57176                         }
57177                         var markup = ct.apply(p);
57178                         if(!c.locked){
57179                             cb+= markup;
57180                         }else{
57181                             lcb+= markup;
57182                         }
57183                     }
57184                     var alt = [];
57185                     if(stripe && ((rowIndex+1) % 2 == 0)){
57186                         alt.push("x-grid-row-alt")
57187                     }
57188                     if(r.dirty){
57189                         alt.push(  " x-grid-dirty-row");
57190                     }
57191                     rp.cells = lcb;
57192                     if(this.getRowClass){
57193                         alt.push(this.getRowClass(r, rowIndex));
57194                     }
57195                     if (hasListener) {
57196                         rowcfg = {
57197                              
57198                             record: r,
57199                             rowIndex : rowIndex,
57200                             rowClass : ''
57201                         };
57202                         this.grid.fireEvent('rowclass', this, rowcfg);
57203                         alt.push(rowcfg.rowClass);
57204                     }
57205                     rp.alt = alt.join(" ");
57206                     lbuf+= rt.apply(rp);
57207                     rp.cells = cb;
57208                     buf+=  rt.apply(rp);
57209                 }
57210                 return [lbuf, buf];
57211             } :
57212             function(cs, rs, ds, startRow, colCount, stripe){
57213                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57214                 // buffers
57215                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57216                 var hasListener = this.grid.hasListener('rowclass');
57217  
57218                 var rowcfg = {};
57219                 for(var j = 0, len = rs.length; j < len; j++){
57220                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57221                     for(var i = 0; i < colCount; i++){
57222                         c = cs[i];
57223                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57224                         p.id = c.id;
57225                         p.css = p.attr = "";
57226                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57227                         if(p.value == undefined || p.value === "") {
57228                             p.value = "&#160;";
57229                         }
57230                         //Roo.log(c);
57231                          if(c.has_editor){
57232                             p.css += ' x-grid-editable-cell';
57233                         }
57234                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57235                             p.css += ' x-grid-dirty-cell' 
57236                         }
57237                         
57238                         var markup = ct.apply(p);
57239                         if(!c.locked){
57240                             cb[cb.length] = markup;
57241                         }else{
57242                             lcb[lcb.length] = markup;
57243                         }
57244                     }
57245                     var alt = [];
57246                     if(stripe && ((rowIndex+1) % 2 == 0)){
57247                         alt.push( "x-grid-row-alt");
57248                     }
57249                     if(r.dirty){
57250                         alt.push(" x-grid-dirty-row");
57251                     }
57252                     rp.cells = lcb;
57253                     if(this.getRowClass){
57254                         alt.push( this.getRowClass(r, rowIndex));
57255                     }
57256                     if (hasListener) {
57257                         rowcfg = {
57258                              
57259                             record: r,
57260                             rowIndex : rowIndex,
57261                             rowClass : ''
57262                         };
57263                         this.grid.fireEvent('rowclass', this, rowcfg);
57264                         alt.push(rowcfg.rowClass);
57265                     }
57266                     
57267                     rp.alt = alt.join(" ");
57268                     rp.cells = lcb.join("");
57269                     lbuf[lbuf.length] = rt.apply(rp);
57270                     rp.cells = cb.join("");
57271                     buf[buf.length] =  rt.apply(rp);
57272                 }
57273                 return [lbuf.join(""), buf.join("")];
57274             },
57275
57276     renderBody : function(){
57277         var markup = this.renderRows();
57278         var bt = this.templates.body;
57279         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57280     },
57281
57282     /**
57283      * Refreshes the grid
57284      * @param {Boolean} headersToo
57285      */
57286     refresh : function(headersToo){
57287         this.fireEvent("beforerefresh", this);
57288         this.grid.stopEditing();
57289         var result = this.renderBody();
57290         this.lockedBody.update(result[0]);
57291         this.mainBody.update(result[1]);
57292         if(headersToo === true){
57293             this.updateHeaders();
57294             this.updateColumns();
57295             this.updateSplitters();
57296             this.updateHeaderSortState();
57297         }
57298         this.syncRowHeights();
57299         this.layout();
57300         this.fireEvent("refresh", this);
57301     },
57302
57303     handleColumnMove : function(cm, oldIndex, newIndex){
57304         this.indexMap = null;
57305         var s = this.getScrollState();
57306         this.refresh(true);
57307         this.restoreScroll(s);
57308         this.afterMove(newIndex);
57309     },
57310
57311     afterMove : function(colIndex){
57312         if(this.enableMoveAnim && Roo.enableFx){
57313             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57314         }
57315         // if multisort - fix sortOrder, and reload..
57316         if (this.grid.dataSource.multiSort) {
57317             // the we can call sort again..
57318             var dm = this.grid.dataSource;
57319             var cm = this.grid.colModel;
57320             var so = [];
57321             for(var i = 0; i < cm.config.length; i++ ) {
57322                 
57323                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57324                     continue; // dont' bother, it's not in sort list or being set.
57325                 }
57326                 
57327                 so.push(cm.config[i].dataIndex);
57328             };
57329             dm.sortOrder = so;
57330             dm.load(dm.lastOptions);
57331             
57332             
57333         }
57334         
57335     },
57336
57337     updateCell : function(dm, rowIndex, dataIndex){
57338         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57339         if(typeof colIndex == "undefined"){ // not present in grid
57340             return;
57341         }
57342         var cm = this.grid.colModel;
57343         var cell = this.getCell(rowIndex, colIndex);
57344         var cellText = this.getCellText(rowIndex, colIndex);
57345
57346         var p = {
57347             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57348             id : cm.getColumnId(colIndex),
57349             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57350         };
57351         var renderer = cm.getRenderer(colIndex);
57352         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57353         if(typeof val == "undefined" || val === "") {
57354             val = "&#160;";
57355         }
57356         cellText.innerHTML = val;
57357         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57358         this.syncRowHeights(rowIndex, rowIndex);
57359     },
57360
57361     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57362         var maxWidth = 0;
57363         if(this.grid.autoSizeHeaders){
57364             var h = this.getHeaderCellMeasure(colIndex);
57365             maxWidth = Math.max(maxWidth, h.scrollWidth);
57366         }
57367         var tb, index;
57368         if(this.cm.isLocked(colIndex)){
57369             tb = this.getLockedTable();
57370             index = colIndex;
57371         }else{
57372             tb = this.getBodyTable();
57373             index = colIndex - this.cm.getLockedCount();
57374         }
57375         if(tb && tb.rows){
57376             var rows = tb.rows;
57377             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57378             for(var i = 0; i < stopIndex; i++){
57379                 var cell = rows[i].childNodes[index].firstChild;
57380                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57381             }
57382         }
57383         return maxWidth + /*margin for error in IE*/ 5;
57384     },
57385     /**
57386      * Autofit a column to its content.
57387      * @param {Number} colIndex
57388      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57389      */
57390      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57391          if(this.cm.isHidden(colIndex)){
57392              return; // can't calc a hidden column
57393          }
57394         if(forceMinSize){
57395             var cid = this.cm.getColumnId(colIndex);
57396             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57397            if(this.grid.autoSizeHeaders){
57398                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57399            }
57400         }
57401         var newWidth = this.calcColumnWidth(colIndex);
57402         this.cm.setColumnWidth(colIndex,
57403             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57404         if(!suppressEvent){
57405             this.grid.fireEvent("columnresize", colIndex, newWidth);
57406         }
57407     },
57408
57409     /**
57410      * Autofits all columns to their content and then expands to fit any extra space in the grid
57411      */
57412      autoSizeColumns : function(){
57413         var cm = this.grid.colModel;
57414         var colCount = cm.getColumnCount();
57415         for(var i = 0; i < colCount; i++){
57416             this.autoSizeColumn(i, true, true);
57417         }
57418         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57419             this.fitColumns();
57420         }else{
57421             this.updateColumns();
57422             this.layout();
57423         }
57424     },
57425
57426     /**
57427      * Autofits all columns to the grid's width proportionate with their current size
57428      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57429      */
57430     fitColumns : function(reserveScrollSpace){
57431         var cm = this.grid.colModel;
57432         var colCount = cm.getColumnCount();
57433         var cols = [];
57434         var width = 0;
57435         var i, w;
57436         for (i = 0; i < colCount; i++){
57437             if(!cm.isHidden(i) && !cm.isFixed(i)){
57438                 w = cm.getColumnWidth(i);
57439                 cols.push(i);
57440                 cols.push(w);
57441                 width += w;
57442             }
57443         }
57444         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57445         if(reserveScrollSpace){
57446             avail -= 17;
57447         }
57448         var frac = (avail - cm.getTotalWidth())/width;
57449         while (cols.length){
57450             w = cols.pop();
57451             i = cols.pop();
57452             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57453         }
57454         this.updateColumns();
57455         this.layout();
57456     },
57457
57458     onRowSelect : function(rowIndex){
57459         var row = this.getRowComposite(rowIndex);
57460         row.addClass("x-grid-row-selected");
57461     },
57462
57463     onRowDeselect : function(rowIndex){
57464         var row = this.getRowComposite(rowIndex);
57465         row.removeClass("x-grid-row-selected");
57466     },
57467
57468     onCellSelect : function(row, col){
57469         var cell = this.getCell(row, col);
57470         if(cell){
57471             Roo.fly(cell).addClass("x-grid-cell-selected");
57472         }
57473     },
57474
57475     onCellDeselect : function(row, col){
57476         var cell = this.getCell(row, col);
57477         if(cell){
57478             Roo.fly(cell).removeClass("x-grid-cell-selected");
57479         }
57480     },
57481
57482     updateHeaderSortState : function(){
57483         
57484         // sort state can be single { field: xxx, direction : yyy}
57485         // or   { xxx=>ASC , yyy : DESC ..... }
57486         
57487         var mstate = {};
57488         if (!this.ds.multiSort) { 
57489             var state = this.ds.getSortState();
57490             if(!state){
57491                 return;
57492             }
57493             mstate[state.field] = state.direction;
57494             // FIXME... - this is not used here.. but might be elsewhere..
57495             this.sortState = state;
57496             
57497         } else {
57498             mstate = this.ds.sortToggle;
57499         }
57500         //remove existing sort classes..
57501         
57502         var sc = this.sortClasses;
57503         var hds = this.el.select(this.headerSelector).removeClass(sc);
57504         
57505         for(var f in mstate) {
57506         
57507             var sortColumn = this.cm.findColumnIndex(f);
57508             
57509             if(sortColumn != -1){
57510                 var sortDir = mstate[f];        
57511                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57512             }
57513         }
57514         
57515          
57516         
57517     },
57518
57519
57520     handleHeaderClick : function(g, index,e){
57521         
57522         Roo.log("header click");
57523         
57524         if (Roo.isTouch) {
57525             // touch events on header are handled by context
57526             this.handleHdCtx(g,index,e);
57527             return;
57528         }
57529         
57530         
57531         if(this.headersDisabled){
57532             return;
57533         }
57534         var dm = g.dataSource, cm = g.colModel;
57535         if(!cm.isSortable(index)){
57536             return;
57537         }
57538         g.stopEditing();
57539         
57540         if (dm.multiSort) {
57541             // update the sortOrder
57542             var so = [];
57543             for(var i = 0; i < cm.config.length; i++ ) {
57544                 
57545                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57546                     continue; // dont' bother, it's not in sort list or being set.
57547                 }
57548                 
57549                 so.push(cm.config[i].dataIndex);
57550             };
57551             dm.sortOrder = so;
57552         }
57553         
57554         
57555         dm.sort(cm.getDataIndex(index));
57556     },
57557
57558
57559     destroy : function(){
57560         if(this.colMenu){
57561             this.colMenu.removeAll();
57562             Roo.menu.MenuMgr.unregister(this.colMenu);
57563             this.colMenu.getEl().remove();
57564             delete this.colMenu;
57565         }
57566         if(this.hmenu){
57567             this.hmenu.removeAll();
57568             Roo.menu.MenuMgr.unregister(this.hmenu);
57569             this.hmenu.getEl().remove();
57570             delete this.hmenu;
57571         }
57572         if(this.grid.enableColumnMove){
57573             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57574             if(dds){
57575                 for(var dd in dds){
57576                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57577                         var elid = dds[dd].dragElId;
57578                         dds[dd].unreg();
57579                         Roo.get(elid).remove();
57580                     } else if(dds[dd].config.isTarget){
57581                         dds[dd].proxyTop.remove();
57582                         dds[dd].proxyBottom.remove();
57583                         dds[dd].unreg();
57584                     }
57585                     if(Roo.dd.DDM.locationCache[dd]){
57586                         delete Roo.dd.DDM.locationCache[dd];
57587                     }
57588                 }
57589                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57590             }
57591         }
57592         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57593         this.bind(null, null);
57594         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57595     },
57596
57597     handleLockChange : function(){
57598         this.refresh(true);
57599     },
57600
57601     onDenyColumnLock : function(){
57602
57603     },
57604
57605     onDenyColumnHide : function(){
57606
57607     },
57608
57609     handleHdMenuClick : function(item){
57610         var index = this.hdCtxIndex;
57611         var cm = this.cm, ds = this.ds;
57612         switch(item.id){
57613             case "asc":
57614                 ds.sort(cm.getDataIndex(index), "ASC");
57615                 break;
57616             case "desc":
57617                 ds.sort(cm.getDataIndex(index), "DESC");
57618                 break;
57619             case "lock":
57620                 var lc = cm.getLockedCount();
57621                 if(cm.getColumnCount(true) <= lc+1){
57622                     this.onDenyColumnLock();
57623                     return;
57624                 }
57625                 if(lc != index){
57626                     cm.setLocked(index, true, true);
57627                     cm.moveColumn(index, lc);
57628                     this.grid.fireEvent("columnmove", index, lc);
57629                 }else{
57630                     cm.setLocked(index, true);
57631                 }
57632             break;
57633             case "unlock":
57634                 var lc = cm.getLockedCount();
57635                 if((lc-1) != index){
57636                     cm.setLocked(index, false, true);
57637                     cm.moveColumn(index, lc-1);
57638                     this.grid.fireEvent("columnmove", index, lc-1);
57639                 }else{
57640                     cm.setLocked(index, false);
57641                 }
57642             break;
57643             case 'wider': // used to expand cols on touch..
57644             case 'narrow':
57645                 var cw = cm.getColumnWidth(index);
57646                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57647                 cw = Math.max(0, cw);
57648                 cw = Math.min(cw,4000);
57649                 cm.setColumnWidth(index, cw);
57650                 break;
57651                 
57652             default:
57653                 index = cm.getIndexById(item.id.substr(4));
57654                 if(index != -1){
57655                     if(item.checked && cm.getColumnCount(true) <= 1){
57656                         this.onDenyColumnHide();
57657                         return false;
57658                     }
57659                     cm.setHidden(index, item.checked);
57660                 }
57661         }
57662         return true;
57663     },
57664
57665     beforeColMenuShow : function(){
57666         var cm = this.cm,  colCount = cm.getColumnCount();
57667         this.colMenu.removeAll();
57668         for(var i = 0; i < colCount; i++){
57669             this.colMenu.add(new Roo.menu.CheckItem({
57670                 id: "col-"+cm.getColumnId(i),
57671                 text: cm.getColumnHeader(i),
57672                 checked: !cm.isHidden(i),
57673                 hideOnClick:false
57674             }));
57675         }
57676     },
57677
57678     handleHdCtx : function(g, index, e){
57679         e.stopEvent();
57680         var hd = this.getHeaderCell(index);
57681         this.hdCtxIndex = index;
57682         var ms = this.hmenu.items, cm = this.cm;
57683         ms.get("asc").setDisabled(!cm.isSortable(index));
57684         ms.get("desc").setDisabled(!cm.isSortable(index));
57685         if(this.grid.enableColLock !== false){
57686             ms.get("lock").setDisabled(cm.isLocked(index));
57687             ms.get("unlock").setDisabled(!cm.isLocked(index));
57688         }
57689         this.hmenu.show(hd, "tl-bl");
57690     },
57691
57692     handleHdOver : function(e){
57693         var hd = this.findHeaderCell(e.getTarget());
57694         if(hd && !this.headersDisabled){
57695             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57696                this.fly(hd).addClass("x-grid-hd-over");
57697             }
57698         }
57699     },
57700
57701     handleHdOut : function(e){
57702         var hd = this.findHeaderCell(e.getTarget());
57703         if(hd){
57704             this.fly(hd).removeClass("x-grid-hd-over");
57705         }
57706     },
57707
57708     handleSplitDblClick : function(e, t){
57709         var i = this.getCellIndex(t);
57710         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57711             this.autoSizeColumn(i, true);
57712             this.layout();
57713         }
57714     },
57715
57716     render : function(){
57717
57718         var cm = this.cm;
57719         var colCount = cm.getColumnCount();
57720
57721         if(this.grid.monitorWindowResize === true){
57722             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57723         }
57724         var header = this.renderHeaders();
57725         var body = this.templates.body.apply({rows:""});
57726         var html = this.templates.master.apply({
57727             lockedBody: body,
57728             body: body,
57729             lockedHeader: header[0],
57730             header: header[1]
57731         });
57732
57733         //this.updateColumns();
57734
57735         this.grid.getGridEl().dom.innerHTML = html;
57736
57737         this.initElements();
57738         
57739         // a kludge to fix the random scolling effect in webkit
57740         this.el.on("scroll", function() {
57741             this.el.dom.scrollTop=0; // hopefully not recursive..
57742         },this);
57743
57744         this.scroller.on("scroll", this.handleScroll, this);
57745         this.lockedBody.on("mousewheel", this.handleWheel, this);
57746         this.mainBody.on("mousewheel", this.handleWheel, this);
57747
57748         this.mainHd.on("mouseover", this.handleHdOver, this);
57749         this.mainHd.on("mouseout", this.handleHdOut, this);
57750         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57751                 {delegate: "."+this.splitClass});
57752
57753         this.lockedHd.on("mouseover", this.handleHdOver, this);
57754         this.lockedHd.on("mouseout", this.handleHdOut, this);
57755         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57756                 {delegate: "."+this.splitClass});
57757
57758         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57759             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57760         }
57761
57762         this.updateSplitters();
57763
57764         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57765             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57766             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57767         }
57768
57769         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57770             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57771             this.hmenu.add(
57772                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57773                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57774             );
57775             if(this.grid.enableColLock !== false){
57776                 this.hmenu.add('-',
57777                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57778                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57779                 );
57780             }
57781             if (Roo.isTouch) {
57782                  this.hmenu.add('-',
57783                     {id:"wider", text: this.columnsWiderText},
57784                     {id:"narrow", text: this.columnsNarrowText }
57785                 );
57786                 
57787                  
57788             }
57789             
57790             if(this.grid.enableColumnHide !== false){
57791
57792                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57793                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57794                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57795
57796                 this.hmenu.add('-',
57797                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57798                 );
57799             }
57800             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57801
57802             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57803         }
57804
57805         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57806             this.dd = new Roo.grid.GridDragZone(this.grid, {
57807                 ddGroup : this.grid.ddGroup || 'GridDD'
57808             });
57809             
57810         }
57811
57812         /*
57813         for(var i = 0; i < colCount; i++){
57814             if(cm.isHidden(i)){
57815                 this.hideColumn(i);
57816             }
57817             if(cm.config[i].align){
57818                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57819                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57820             }
57821         }*/
57822         
57823         this.updateHeaderSortState();
57824
57825         this.beforeInitialResize();
57826         this.layout(true);
57827
57828         // two part rendering gives faster view to the user
57829         this.renderPhase2.defer(1, this);
57830     },
57831
57832     renderPhase2 : function(){
57833         // render the rows now
57834         this.refresh();
57835         if(this.grid.autoSizeColumns){
57836             this.autoSizeColumns();
57837         }
57838     },
57839
57840     beforeInitialResize : function(){
57841
57842     },
57843
57844     onColumnSplitterMoved : function(i, w){
57845         this.userResized = true;
57846         var cm = this.grid.colModel;
57847         cm.setColumnWidth(i, w, true);
57848         var cid = cm.getColumnId(i);
57849         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57850         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57851         this.updateSplitters();
57852         this.layout();
57853         this.grid.fireEvent("columnresize", i, w);
57854     },
57855
57856     syncRowHeights : function(startIndex, endIndex){
57857         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57858             startIndex = startIndex || 0;
57859             var mrows = this.getBodyTable().rows;
57860             var lrows = this.getLockedTable().rows;
57861             var len = mrows.length-1;
57862             endIndex = Math.min(endIndex || len, len);
57863             for(var i = startIndex; i <= endIndex; i++){
57864                 var m = mrows[i], l = lrows[i];
57865                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57866                 m.style.height = l.style.height = h + "px";
57867             }
57868         }
57869     },
57870
57871     layout : function(initialRender, is2ndPass)
57872     {
57873         var g = this.grid;
57874         var auto = g.autoHeight;
57875         var scrollOffset = 16;
57876         var c = g.getGridEl(), cm = this.cm,
57877                 expandCol = g.autoExpandColumn,
57878                 gv = this;
57879         //c.beginMeasure();
57880
57881         if(!c.dom.offsetWidth){ // display:none?
57882             if(initialRender){
57883                 this.lockedWrap.show();
57884                 this.mainWrap.show();
57885             }
57886             return;
57887         }
57888
57889         var hasLock = this.cm.isLocked(0);
57890
57891         var tbh = this.headerPanel.getHeight();
57892         var bbh = this.footerPanel.getHeight();
57893
57894         if(auto){
57895             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57896             var newHeight = ch + c.getBorderWidth("tb");
57897             if(g.maxHeight){
57898                 newHeight = Math.min(g.maxHeight, newHeight);
57899             }
57900             c.setHeight(newHeight);
57901         }
57902
57903         if(g.autoWidth){
57904             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57905         }
57906
57907         var s = this.scroller;
57908
57909         var csize = c.getSize(true);
57910
57911         this.el.setSize(csize.width, csize.height);
57912
57913         this.headerPanel.setWidth(csize.width);
57914         this.footerPanel.setWidth(csize.width);
57915
57916         var hdHeight = this.mainHd.getHeight();
57917         var vw = csize.width;
57918         var vh = csize.height - (tbh + bbh);
57919
57920         s.setSize(vw, vh);
57921
57922         var bt = this.getBodyTable();
57923         
57924         if(cm.getLockedCount() == cm.config.length){
57925             bt = this.getLockedTable();
57926         }
57927         
57928         var ltWidth = hasLock ?
57929                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57930
57931         var scrollHeight = bt.offsetHeight;
57932         var scrollWidth = ltWidth + bt.offsetWidth;
57933         var vscroll = false, hscroll = false;
57934
57935         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57936
57937         var lw = this.lockedWrap, mw = this.mainWrap;
57938         var lb = this.lockedBody, mb = this.mainBody;
57939
57940         setTimeout(function(){
57941             var t = s.dom.offsetTop;
57942             var w = s.dom.clientWidth,
57943                 h = s.dom.clientHeight;
57944
57945             lw.setTop(t);
57946             lw.setSize(ltWidth, h);
57947
57948             mw.setLeftTop(ltWidth, t);
57949             mw.setSize(w-ltWidth, h);
57950
57951             lb.setHeight(h-hdHeight);
57952             mb.setHeight(h-hdHeight);
57953
57954             if(is2ndPass !== true && !gv.userResized && expandCol){
57955                 // high speed resize without full column calculation
57956                 
57957                 var ci = cm.getIndexById(expandCol);
57958                 if (ci < 0) {
57959                     ci = cm.findColumnIndex(expandCol);
57960                 }
57961                 ci = Math.max(0, ci); // make sure it's got at least the first col.
57962                 var expandId = cm.getColumnId(ci);
57963                 var  tw = cm.getTotalWidth(false);
57964                 var currentWidth = cm.getColumnWidth(ci);
57965                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
57966                 if(currentWidth != cw){
57967                     cm.setColumnWidth(ci, cw, true);
57968                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57969                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
57970                     gv.updateSplitters();
57971                     gv.layout(false, true);
57972                 }
57973             }
57974
57975             if(initialRender){
57976                 lw.show();
57977                 mw.show();
57978             }
57979             //c.endMeasure();
57980         }, 10);
57981     },
57982
57983     onWindowResize : function(){
57984         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
57985             return;
57986         }
57987         this.layout();
57988     },
57989
57990     appendFooter : function(parentEl){
57991         return null;
57992     },
57993
57994     sortAscText : "Sort Ascending",
57995     sortDescText : "Sort Descending",
57996     lockText : "Lock Column",
57997     unlockText : "Unlock Column",
57998     columnsText : "Columns",
57999  
58000     columnsWiderText : "Wider",
58001     columnsNarrowText : "Thinner"
58002 });
58003
58004
58005 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58006     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58007     this.proxy.el.addClass('x-grid3-col-dd');
58008 };
58009
58010 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58011     handleMouseDown : function(e){
58012
58013     },
58014
58015     callHandleMouseDown : function(e){
58016         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58017     }
58018 });
58019 /*
58020  * Based on:
58021  * Ext JS Library 1.1.1
58022  * Copyright(c) 2006-2007, Ext JS, LLC.
58023  *
58024  * Originally Released Under LGPL - original licence link has changed is not relivant.
58025  *
58026  * Fork - LGPL
58027  * <script type="text/javascript">
58028  */
58029  
58030 // private
58031 // This is a support class used internally by the Grid components
58032 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58033     this.grid = grid;
58034     this.view = grid.getView();
58035     this.proxy = this.view.resizeProxy;
58036     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
58037         "gridSplitters" + this.grid.getGridEl().id, {
58038         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
58039     });
58040     this.setHandleElId(Roo.id(hd));
58041     this.setOuterHandleElId(Roo.id(hd2));
58042     this.scroll = false;
58043 };
58044 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58045     fly: Roo.Element.fly,
58046
58047     b4StartDrag : function(x, y){
58048         this.view.headersDisabled = true;
58049         this.proxy.setHeight(this.view.mainWrap.getHeight());
58050         var w = this.cm.getColumnWidth(this.cellIndex);
58051         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58052         this.resetConstraints();
58053         this.setXConstraint(minw, 1000);
58054         this.setYConstraint(0, 0);
58055         this.minX = x - minw;
58056         this.maxX = x + 1000;
58057         this.startPos = x;
58058         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58059     },
58060
58061
58062     handleMouseDown : function(e){
58063         ev = Roo.EventObject.setEvent(e);
58064         var t = this.fly(ev.getTarget());
58065         if(t.hasClass("x-grid-split")){
58066             this.cellIndex = this.view.getCellIndex(t.dom);
58067             this.split = t.dom;
58068             this.cm = this.grid.colModel;
58069             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58070                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58071             }
58072         }
58073     },
58074
58075     endDrag : function(e){
58076         this.view.headersDisabled = false;
58077         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58078         var diff = endX - this.startPos;
58079         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
58080     },
58081
58082     autoOffset : function(){
58083         this.setDelta(0,0);
58084     }
58085 });/*
58086  * Based on:
58087  * Ext JS Library 1.1.1
58088  * Copyright(c) 2006-2007, Ext JS, LLC.
58089  *
58090  * Originally Released Under LGPL - original licence link has changed is not relivant.
58091  *
58092  * Fork - LGPL
58093  * <script type="text/javascript">
58094  */
58095  
58096 // private
58097 // This is a support class used internally by the Grid components
58098 Roo.grid.GridDragZone = function(grid, config){
58099     this.view = grid.getView();
58100     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58101     if(this.view.lockedBody){
58102         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58103         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58104     }
58105     this.scroll = false;
58106     this.grid = grid;
58107     this.ddel = document.createElement('div');
58108     this.ddel.className = 'x-grid-dd-wrap';
58109 };
58110
58111 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58112     ddGroup : "GridDD",
58113
58114     getDragData : function(e){
58115         var t = Roo.lib.Event.getTarget(e);
58116         var rowIndex = this.view.findRowIndex(t);
58117         var sm = this.grid.selModel;
58118             
58119         //Roo.log(rowIndex);
58120         
58121         if (sm.getSelectedCell) {
58122             // cell selection..
58123             if (!sm.getSelectedCell()) {
58124                 return false;
58125             }
58126             if (rowIndex != sm.getSelectedCell()[0]) {
58127                 return false;
58128             }
58129         
58130         }
58131         if (sm.getSelections && sm.getSelections().length < 1) {
58132             return false;
58133         }
58134         
58135         
58136         // before it used to all dragging of unseleted... - now we dont do that.
58137         if(rowIndex !== false){
58138             
58139             // if editorgrid.. 
58140             
58141             
58142             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58143                
58144             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58145               //  
58146             //}
58147             if (e.hasModifier()){
58148                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58149             }
58150             
58151             Roo.log("getDragData");
58152             
58153             return {
58154                 grid: this.grid,
58155                 ddel: this.ddel,
58156                 rowIndex: rowIndex,
58157                 selections: sm.getSelections ? sm.getSelections() : (
58158                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58159             };
58160         }
58161         return false;
58162     },
58163     
58164     
58165     onInitDrag : function(e){
58166         var data = this.dragData;
58167         this.ddel.innerHTML = this.grid.getDragDropText();
58168         this.proxy.update(this.ddel);
58169         // fire start drag?
58170     },
58171
58172     afterRepair : function(){
58173         this.dragging = false;
58174     },
58175
58176     getRepairXY : function(e, data){
58177         return false;
58178     },
58179
58180     onEndDrag : function(data, e){
58181         // fire end drag?
58182     },
58183
58184     onValidDrop : function(dd, e, id){
58185         // fire drag drop?
58186         this.hideProxy();
58187     },
58188
58189     beforeInvalidDrop : function(e, id){
58190
58191     }
58192 });/*
58193  * Based on:
58194  * Ext JS Library 1.1.1
58195  * Copyright(c) 2006-2007, Ext JS, LLC.
58196  *
58197  * Originally Released Under LGPL - original licence link has changed is not relivant.
58198  *
58199  * Fork - LGPL
58200  * <script type="text/javascript">
58201  */
58202  
58203
58204 /**
58205  * @class Roo.grid.ColumnModel
58206  * @extends Roo.util.Observable
58207  * This is the default implementation of a ColumnModel used by the Grid. It defines
58208  * the columns in the grid.
58209  * <br>Usage:<br>
58210  <pre><code>
58211  var colModel = new Roo.grid.ColumnModel([
58212         {header: "Ticker", width: 60, sortable: true, locked: true},
58213         {header: "Company Name", width: 150, sortable: true},
58214         {header: "Market Cap.", width: 100, sortable: true},
58215         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58216         {header: "Employees", width: 100, sortable: true, resizable: false}
58217  ]);
58218  </code></pre>
58219  * <p>
58220  
58221  * The config options listed for this class are options which may appear in each
58222  * individual column definition.
58223  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58224  * @constructor
58225  * @param {Object} config An Array of column config objects. See this class's
58226  * config objects for details.
58227 */
58228 Roo.grid.ColumnModel = function(config){
58229         /**
58230      * The config passed into the constructor
58231      */
58232     this.config = config;
58233     this.lookup = {};
58234
58235     // if no id, create one
58236     // if the column does not have a dataIndex mapping,
58237     // map it to the order it is in the config
58238     for(var i = 0, len = config.length; i < len; i++){
58239         var c = config[i];
58240         if(typeof c.dataIndex == "undefined"){
58241             c.dataIndex = i;
58242         }
58243         if(typeof c.renderer == "string"){
58244             c.renderer = Roo.util.Format[c.renderer];
58245         }
58246         if(typeof c.id == "undefined"){
58247             c.id = Roo.id();
58248         }
58249         if(c.editor && c.editor.xtype){
58250             c.editor  = Roo.factory(c.editor, Roo.grid);
58251         }
58252         if(c.editor && c.editor.isFormField){
58253             c.editor = new Roo.grid.GridEditor(c.editor);
58254         }
58255         this.lookup[c.id] = c;
58256     }
58257
58258     /**
58259      * The width of columns which have no width specified (defaults to 100)
58260      * @type Number
58261      */
58262     this.defaultWidth = 100;
58263
58264     /**
58265      * Default sortable of columns which have no sortable specified (defaults to false)
58266      * @type Boolean
58267      */
58268     this.defaultSortable = false;
58269
58270     this.addEvents({
58271         /**
58272              * @event widthchange
58273              * Fires when the width of a column changes.
58274              * @param {ColumnModel} this
58275              * @param {Number} columnIndex The column index
58276              * @param {Number} newWidth The new width
58277              */
58278             "widthchange": true,
58279         /**
58280              * @event headerchange
58281              * Fires when the text of a header changes.
58282              * @param {ColumnModel} this
58283              * @param {Number} columnIndex The column index
58284              * @param {Number} newText The new header text
58285              */
58286             "headerchange": true,
58287         /**
58288              * @event hiddenchange
58289              * Fires when a column is hidden or "unhidden".
58290              * @param {ColumnModel} this
58291              * @param {Number} columnIndex The column index
58292              * @param {Boolean} hidden true if hidden, false otherwise
58293              */
58294             "hiddenchange": true,
58295             /**
58296          * @event columnmoved
58297          * Fires when a column is moved.
58298          * @param {ColumnModel} this
58299          * @param {Number} oldIndex
58300          * @param {Number} newIndex
58301          */
58302         "columnmoved" : true,
58303         /**
58304          * @event columlockchange
58305          * Fires when a column's locked state is changed
58306          * @param {ColumnModel} this
58307          * @param {Number} colIndex
58308          * @param {Boolean} locked true if locked
58309          */
58310         "columnlockchange" : true
58311     });
58312     Roo.grid.ColumnModel.superclass.constructor.call(this);
58313 };
58314 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58315     /**
58316      * @cfg {String} header The header text to display in the Grid view.
58317      */
58318     /**
58319      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58320      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58321      * specified, the column's index is used as an index into the Record's data Array.
58322      */
58323     /**
58324      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58325      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58326      */
58327     /**
58328      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58329      * Defaults to the value of the {@link #defaultSortable} property.
58330      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58331      */
58332     /**
58333      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58334      */
58335     /**
58336      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58337      */
58338     /**
58339      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58340      */
58341     /**
58342      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58343      */
58344     /**
58345      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58346      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58347      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58348      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58349      */
58350        /**
58351      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58352      */
58353     /**
58354      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58355      */
58356     /**
58357      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58358      */
58359     /**
58360      * @cfg {String} cursor (Optional)
58361      */
58362     /**
58363      * @cfg {String} tooltip (Optional)
58364      */
58365     /**
58366      * @cfg {Number} xs (Optional)
58367      */
58368     /**
58369      * @cfg {Number} sm (Optional)
58370      */
58371     /**
58372      * @cfg {Number} md (Optional)
58373      */
58374     /**
58375      * @cfg {Number} lg (Optional)
58376      */
58377     /**
58378      * Returns the id of the column at the specified index.
58379      * @param {Number} index The column index
58380      * @return {String} the id
58381      */
58382     getColumnId : function(index){
58383         return this.config[index].id;
58384     },
58385
58386     /**
58387      * Returns the column for a specified id.
58388      * @param {String} id The column id
58389      * @return {Object} the column
58390      */
58391     getColumnById : function(id){
58392         return this.lookup[id];
58393     },
58394
58395     
58396     /**
58397      * Returns the column for a specified dataIndex.
58398      * @param {String} dataIndex The column dataIndex
58399      * @return {Object|Boolean} the column or false if not found
58400      */
58401     getColumnByDataIndex: function(dataIndex){
58402         var index = this.findColumnIndex(dataIndex);
58403         return index > -1 ? this.config[index] : false;
58404     },
58405     
58406     /**
58407      * Returns the index for a specified column id.
58408      * @param {String} id The column id
58409      * @return {Number} the index, or -1 if not found
58410      */
58411     getIndexById : function(id){
58412         for(var i = 0, len = this.config.length; i < len; i++){
58413             if(this.config[i].id == id){
58414                 return i;
58415             }
58416         }
58417         return -1;
58418     },
58419     
58420     /**
58421      * Returns the index for a specified column dataIndex.
58422      * @param {String} dataIndex The column dataIndex
58423      * @return {Number} the index, or -1 if not found
58424      */
58425     
58426     findColumnIndex : function(dataIndex){
58427         for(var i = 0, len = this.config.length; i < len; i++){
58428             if(this.config[i].dataIndex == dataIndex){
58429                 return i;
58430             }
58431         }
58432         return -1;
58433     },
58434     
58435     
58436     moveColumn : function(oldIndex, newIndex){
58437         var c = this.config[oldIndex];
58438         this.config.splice(oldIndex, 1);
58439         this.config.splice(newIndex, 0, c);
58440         this.dataMap = null;
58441         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58442     },
58443
58444     isLocked : function(colIndex){
58445         return this.config[colIndex].locked === true;
58446     },
58447
58448     setLocked : function(colIndex, value, suppressEvent){
58449         if(this.isLocked(colIndex) == value){
58450             return;
58451         }
58452         this.config[colIndex].locked = value;
58453         if(!suppressEvent){
58454             this.fireEvent("columnlockchange", this, colIndex, value);
58455         }
58456     },
58457
58458     getTotalLockedWidth : function(){
58459         var totalWidth = 0;
58460         for(var i = 0; i < this.config.length; i++){
58461             if(this.isLocked(i) && !this.isHidden(i)){
58462                 this.totalWidth += this.getColumnWidth(i);
58463             }
58464         }
58465         return totalWidth;
58466     },
58467
58468     getLockedCount : function(){
58469         for(var i = 0, len = this.config.length; i < len; i++){
58470             if(!this.isLocked(i)){
58471                 return i;
58472             }
58473         }
58474         
58475         return this.config.length;
58476     },
58477
58478     /**
58479      * Returns the number of columns.
58480      * @return {Number}
58481      */
58482     getColumnCount : function(visibleOnly){
58483         if(visibleOnly === true){
58484             var c = 0;
58485             for(var i = 0, len = this.config.length; i < len; i++){
58486                 if(!this.isHidden(i)){
58487                     c++;
58488                 }
58489             }
58490             return c;
58491         }
58492         return this.config.length;
58493     },
58494
58495     /**
58496      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58497      * @param {Function} fn
58498      * @param {Object} scope (optional)
58499      * @return {Array} result
58500      */
58501     getColumnsBy : function(fn, scope){
58502         var r = [];
58503         for(var i = 0, len = this.config.length; i < len; i++){
58504             var c = this.config[i];
58505             if(fn.call(scope||this, c, i) === true){
58506                 r[r.length] = c;
58507             }
58508         }
58509         return r;
58510     },
58511
58512     /**
58513      * Returns true if the specified column is sortable.
58514      * @param {Number} col The column index
58515      * @return {Boolean}
58516      */
58517     isSortable : function(col){
58518         if(typeof this.config[col].sortable == "undefined"){
58519             return this.defaultSortable;
58520         }
58521         return this.config[col].sortable;
58522     },
58523
58524     /**
58525      * Returns the rendering (formatting) function defined for the column.
58526      * @param {Number} col The column index.
58527      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58528      */
58529     getRenderer : function(col){
58530         if(!this.config[col].renderer){
58531             return Roo.grid.ColumnModel.defaultRenderer;
58532         }
58533         return this.config[col].renderer;
58534     },
58535
58536     /**
58537      * Sets the rendering (formatting) function for a column.
58538      * @param {Number} col The column index
58539      * @param {Function} fn The function to use to process the cell's raw data
58540      * to return HTML markup for the grid view. The render function is called with
58541      * the following parameters:<ul>
58542      * <li>Data value.</li>
58543      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58544      * <li>css A CSS style string to apply to the table cell.</li>
58545      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58546      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58547      * <li>Row index</li>
58548      * <li>Column index</li>
58549      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58550      */
58551     setRenderer : function(col, fn){
58552         this.config[col].renderer = fn;
58553     },
58554
58555     /**
58556      * Returns the width for the specified column.
58557      * @param {Number} col The column index
58558      * @return {Number}
58559      */
58560     getColumnWidth : function(col){
58561         return this.config[col].width * 1 || this.defaultWidth;
58562     },
58563
58564     /**
58565      * Sets the width for a column.
58566      * @param {Number} col The column index
58567      * @param {Number} width The new width
58568      */
58569     setColumnWidth : function(col, width, suppressEvent){
58570         this.config[col].width = width;
58571         this.totalWidth = null;
58572         if(!suppressEvent){
58573              this.fireEvent("widthchange", this, col, width);
58574         }
58575     },
58576
58577     /**
58578      * Returns the total width of all columns.
58579      * @param {Boolean} includeHidden True to include hidden column widths
58580      * @return {Number}
58581      */
58582     getTotalWidth : function(includeHidden){
58583         if(!this.totalWidth){
58584             this.totalWidth = 0;
58585             for(var i = 0, len = this.config.length; i < len; i++){
58586                 if(includeHidden || !this.isHidden(i)){
58587                     this.totalWidth += this.getColumnWidth(i);
58588                 }
58589             }
58590         }
58591         return this.totalWidth;
58592     },
58593
58594     /**
58595      * Returns the header for the specified column.
58596      * @param {Number} col The column index
58597      * @return {String}
58598      */
58599     getColumnHeader : function(col){
58600         return this.config[col].header;
58601     },
58602
58603     /**
58604      * Sets the header for a column.
58605      * @param {Number} col The column index
58606      * @param {String} header The new header
58607      */
58608     setColumnHeader : function(col, header){
58609         this.config[col].header = header;
58610         this.fireEvent("headerchange", this, col, header);
58611     },
58612
58613     /**
58614      * Returns the tooltip for the specified column.
58615      * @param {Number} col The column index
58616      * @return {String}
58617      */
58618     getColumnTooltip : function(col){
58619             return this.config[col].tooltip;
58620     },
58621     /**
58622      * Sets the tooltip for a column.
58623      * @param {Number} col The column index
58624      * @param {String} tooltip The new tooltip
58625      */
58626     setColumnTooltip : function(col, tooltip){
58627             this.config[col].tooltip = tooltip;
58628     },
58629
58630     /**
58631      * Returns the dataIndex for the specified column.
58632      * @param {Number} col The column index
58633      * @return {Number}
58634      */
58635     getDataIndex : function(col){
58636         return this.config[col].dataIndex;
58637     },
58638
58639     /**
58640      * Sets the dataIndex for a column.
58641      * @param {Number} col The column index
58642      * @param {Number} dataIndex The new dataIndex
58643      */
58644     setDataIndex : function(col, dataIndex){
58645         this.config[col].dataIndex = dataIndex;
58646     },
58647
58648     
58649     
58650     /**
58651      * Returns true if the cell is editable.
58652      * @param {Number} colIndex The column index
58653      * @param {Number} rowIndex The row index - this is nto actually used..?
58654      * @return {Boolean}
58655      */
58656     isCellEditable : function(colIndex, rowIndex){
58657         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58658     },
58659
58660     /**
58661      * Returns the editor defined for the cell/column.
58662      * return false or null to disable editing.
58663      * @param {Number} colIndex The column index
58664      * @param {Number} rowIndex The row index
58665      * @return {Object}
58666      */
58667     getCellEditor : function(colIndex, rowIndex){
58668         return this.config[colIndex].editor;
58669     },
58670
58671     /**
58672      * Sets if a column is editable.
58673      * @param {Number} col The column index
58674      * @param {Boolean} editable True if the column is editable
58675      */
58676     setEditable : function(col, editable){
58677         this.config[col].editable = editable;
58678     },
58679
58680
58681     /**
58682      * Returns true if the column is hidden.
58683      * @param {Number} colIndex The column index
58684      * @return {Boolean}
58685      */
58686     isHidden : function(colIndex){
58687         return this.config[colIndex].hidden;
58688     },
58689
58690
58691     /**
58692      * Returns true if the column width cannot be changed
58693      */
58694     isFixed : function(colIndex){
58695         return this.config[colIndex].fixed;
58696     },
58697
58698     /**
58699      * Returns true if the column can be resized
58700      * @return {Boolean}
58701      */
58702     isResizable : function(colIndex){
58703         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58704     },
58705     /**
58706      * Sets if a column is hidden.
58707      * @param {Number} colIndex The column index
58708      * @param {Boolean} hidden True if the column is hidden
58709      */
58710     setHidden : function(colIndex, hidden){
58711         this.config[colIndex].hidden = hidden;
58712         this.totalWidth = null;
58713         this.fireEvent("hiddenchange", this, colIndex, hidden);
58714     },
58715
58716     /**
58717      * Sets the editor for a column.
58718      * @param {Number} col The column index
58719      * @param {Object} editor The editor object
58720      */
58721     setEditor : function(col, editor){
58722         this.config[col].editor = editor;
58723     }
58724 });
58725
58726 Roo.grid.ColumnModel.defaultRenderer = function(value)
58727 {
58728     if(typeof value == "object") {
58729         return value;
58730     }
58731         if(typeof value == "string" && value.length < 1){
58732             return "&#160;";
58733         }
58734     
58735         return String.format("{0}", value);
58736 };
58737
58738 // Alias for backwards compatibility
58739 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58740 /*
58741  * Based on:
58742  * Ext JS Library 1.1.1
58743  * Copyright(c) 2006-2007, Ext JS, LLC.
58744  *
58745  * Originally Released Under LGPL - original licence link has changed is not relivant.
58746  *
58747  * Fork - LGPL
58748  * <script type="text/javascript">
58749  */
58750
58751 /**
58752  * @class Roo.grid.AbstractSelectionModel
58753  * @extends Roo.util.Observable
58754  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58755  * implemented by descendant classes.  This class should not be directly instantiated.
58756  * @constructor
58757  */
58758 Roo.grid.AbstractSelectionModel = function(){
58759     this.locked = false;
58760     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58761 };
58762
58763 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58764     /** @ignore Called by the grid automatically. Do not call directly. */
58765     init : function(grid){
58766         this.grid = grid;
58767         this.initEvents();
58768     },
58769
58770     /**
58771      * Locks the selections.
58772      */
58773     lock : function(){
58774         this.locked = true;
58775     },
58776
58777     /**
58778      * Unlocks the selections.
58779      */
58780     unlock : function(){
58781         this.locked = false;
58782     },
58783
58784     /**
58785      * Returns true if the selections are locked.
58786      * @return {Boolean}
58787      */
58788     isLocked : function(){
58789         return this.locked;
58790     }
58791 });/*
58792  * Based on:
58793  * Ext JS Library 1.1.1
58794  * Copyright(c) 2006-2007, Ext JS, LLC.
58795  *
58796  * Originally Released Under LGPL - original licence link has changed is not relivant.
58797  *
58798  * Fork - LGPL
58799  * <script type="text/javascript">
58800  */
58801 /**
58802  * @extends Roo.grid.AbstractSelectionModel
58803  * @class Roo.grid.RowSelectionModel
58804  * The default SelectionModel used by {@link Roo.grid.Grid}.
58805  * It supports multiple selections and keyboard selection/navigation. 
58806  * @constructor
58807  * @param {Object} config
58808  */
58809 Roo.grid.RowSelectionModel = function(config){
58810     Roo.apply(this, config);
58811     this.selections = new Roo.util.MixedCollection(false, function(o){
58812         return o.id;
58813     });
58814
58815     this.last = false;
58816     this.lastActive = false;
58817
58818     this.addEvents({
58819         /**
58820              * @event selectionchange
58821              * Fires when the selection changes
58822              * @param {SelectionModel} this
58823              */
58824             "selectionchange" : true,
58825         /**
58826              * @event afterselectionchange
58827              * Fires after the selection changes (eg. by key press or clicking)
58828              * @param {SelectionModel} this
58829              */
58830             "afterselectionchange" : true,
58831         /**
58832              * @event beforerowselect
58833              * Fires when a row is selected being selected, return false to cancel.
58834              * @param {SelectionModel} this
58835              * @param {Number} rowIndex The selected index
58836              * @param {Boolean} keepExisting False if other selections will be cleared
58837              */
58838             "beforerowselect" : true,
58839         /**
58840              * @event rowselect
58841              * Fires when a row is selected.
58842              * @param {SelectionModel} this
58843              * @param {Number} rowIndex The selected index
58844              * @param {Roo.data.Record} r The record
58845              */
58846             "rowselect" : true,
58847         /**
58848              * @event rowdeselect
58849              * Fires when a row is deselected.
58850              * @param {SelectionModel} this
58851              * @param {Number} rowIndex The selected index
58852              */
58853         "rowdeselect" : true
58854     });
58855     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
58856     this.locked = false;
58857 };
58858
58859 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
58860     /**
58861      * @cfg {Boolean} singleSelect
58862      * True to allow selection of only one row at a time (defaults to false)
58863      */
58864     singleSelect : false,
58865
58866     // private
58867     initEvents : function(){
58868
58869         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
58870             this.grid.on("mousedown", this.handleMouseDown, this);
58871         }else{ // allow click to work like normal
58872             this.grid.on("rowclick", this.handleDragableRowClick, this);
58873         }
58874
58875         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
58876             "up" : function(e){
58877                 if(!e.shiftKey){
58878                     this.selectPrevious(e.shiftKey);
58879                 }else if(this.last !== false && this.lastActive !== false){
58880                     var last = this.last;
58881                     this.selectRange(this.last,  this.lastActive-1);
58882                     this.grid.getView().focusRow(this.lastActive);
58883                     if(last !== false){
58884                         this.last = last;
58885                     }
58886                 }else{
58887                     this.selectFirstRow();
58888                 }
58889                 this.fireEvent("afterselectionchange", this);
58890             },
58891             "down" : function(e){
58892                 if(!e.shiftKey){
58893                     this.selectNext(e.shiftKey);
58894                 }else if(this.last !== false && this.lastActive !== false){
58895                     var last = this.last;
58896                     this.selectRange(this.last,  this.lastActive+1);
58897                     this.grid.getView().focusRow(this.lastActive);
58898                     if(last !== false){
58899                         this.last = last;
58900                     }
58901                 }else{
58902                     this.selectFirstRow();
58903                 }
58904                 this.fireEvent("afterselectionchange", this);
58905             },
58906             scope: this
58907         });
58908
58909         var view = this.grid.view;
58910         view.on("refresh", this.onRefresh, this);
58911         view.on("rowupdated", this.onRowUpdated, this);
58912         view.on("rowremoved", this.onRemove, this);
58913     },
58914
58915     // private
58916     onRefresh : function(){
58917         var ds = this.grid.dataSource, i, v = this.grid.view;
58918         var s = this.selections;
58919         s.each(function(r){
58920             if((i = ds.indexOfId(r.id)) != -1){
58921                 v.onRowSelect(i);
58922                 s.add(ds.getAt(i)); // updating the selection relate data
58923             }else{
58924                 s.remove(r);
58925             }
58926         });
58927     },
58928
58929     // private
58930     onRemove : function(v, index, r){
58931         this.selections.remove(r);
58932     },
58933
58934     // private
58935     onRowUpdated : function(v, index, r){
58936         if(this.isSelected(r)){
58937             v.onRowSelect(index);
58938         }
58939     },
58940
58941     /**
58942      * Select records.
58943      * @param {Array} records The records to select
58944      * @param {Boolean} keepExisting (optional) True to keep existing selections
58945      */
58946     selectRecords : function(records, keepExisting){
58947         if(!keepExisting){
58948             this.clearSelections();
58949         }
58950         var ds = this.grid.dataSource;
58951         for(var i = 0, len = records.length; i < len; i++){
58952             this.selectRow(ds.indexOf(records[i]), true);
58953         }
58954     },
58955
58956     /**
58957      * Gets the number of selected rows.
58958      * @return {Number}
58959      */
58960     getCount : function(){
58961         return this.selections.length;
58962     },
58963
58964     /**
58965      * Selects the first row in the grid.
58966      */
58967     selectFirstRow : function(){
58968         this.selectRow(0);
58969     },
58970
58971     /**
58972      * Select the last row.
58973      * @param {Boolean} keepExisting (optional) True to keep existing selections
58974      */
58975     selectLastRow : function(keepExisting){
58976         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
58977     },
58978
58979     /**
58980      * Selects the row immediately following the last selected row.
58981      * @param {Boolean} keepExisting (optional) True to keep existing selections
58982      */
58983     selectNext : function(keepExisting){
58984         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
58985             this.selectRow(this.last+1, keepExisting);
58986             this.grid.getView().focusRow(this.last);
58987         }
58988     },
58989
58990     /**
58991      * Selects the row that precedes the last selected row.
58992      * @param {Boolean} keepExisting (optional) True to keep existing selections
58993      */
58994     selectPrevious : function(keepExisting){
58995         if(this.last){
58996             this.selectRow(this.last-1, keepExisting);
58997             this.grid.getView().focusRow(this.last);
58998         }
58999     },
59000
59001     /**
59002      * Returns the selected records
59003      * @return {Array} Array of selected records
59004      */
59005     getSelections : function(){
59006         return [].concat(this.selections.items);
59007     },
59008
59009     /**
59010      * Returns the first selected record.
59011      * @return {Record}
59012      */
59013     getSelected : function(){
59014         return this.selections.itemAt(0);
59015     },
59016
59017
59018     /**
59019      * Clears all selections.
59020      */
59021     clearSelections : function(fast){
59022         if(this.locked) {
59023             return;
59024         }
59025         if(fast !== true){
59026             var ds = this.grid.dataSource;
59027             var s = this.selections;
59028             s.each(function(r){
59029                 this.deselectRow(ds.indexOfId(r.id));
59030             }, this);
59031             s.clear();
59032         }else{
59033             this.selections.clear();
59034         }
59035         this.last = false;
59036     },
59037
59038
59039     /**
59040      * Selects all rows.
59041      */
59042     selectAll : function(){
59043         if(this.locked) {
59044             return;
59045         }
59046         this.selections.clear();
59047         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
59048             this.selectRow(i, true);
59049         }
59050     },
59051
59052     /**
59053      * Returns True if there is a selection.
59054      * @return {Boolean}
59055      */
59056     hasSelection : function(){
59057         return this.selections.length > 0;
59058     },
59059
59060     /**
59061      * Returns True if the specified row is selected.
59062      * @param {Number/Record} record The record or index of the record to check
59063      * @return {Boolean}
59064      */
59065     isSelected : function(index){
59066         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
59067         return (r && this.selections.key(r.id) ? true : false);
59068     },
59069
59070     /**
59071      * Returns True if the specified record id is selected.
59072      * @param {String} id The id of record to check
59073      * @return {Boolean}
59074      */
59075     isIdSelected : function(id){
59076         return (this.selections.key(id) ? true : false);
59077     },
59078
59079     // private
59080     handleMouseDown : function(e, t){
59081         var view = this.grid.getView(), rowIndex;
59082         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59083             return;
59084         };
59085         if(e.shiftKey && this.last !== false){
59086             var last = this.last;
59087             this.selectRange(last, rowIndex, e.ctrlKey);
59088             this.last = last; // reset the last
59089             view.focusRow(rowIndex);
59090         }else{
59091             var isSelected = this.isSelected(rowIndex);
59092             if(e.button !== 0 && isSelected){
59093                 view.focusRow(rowIndex);
59094             }else if(e.ctrlKey && isSelected){
59095                 this.deselectRow(rowIndex);
59096             }else if(!isSelected){
59097                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59098                 view.focusRow(rowIndex);
59099             }
59100         }
59101         this.fireEvent("afterselectionchange", this);
59102     },
59103     // private
59104     handleDragableRowClick :  function(grid, rowIndex, e) 
59105     {
59106         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59107             this.selectRow(rowIndex, false);
59108             grid.view.focusRow(rowIndex);
59109              this.fireEvent("afterselectionchange", this);
59110         }
59111     },
59112     
59113     /**
59114      * Selects multiple rows.
59115      * @param {Array} rows Array of the indexes of the row to select
59116      * @param {Boolean} keepExisting (optional) True to keep existing selections
59117      */
59118     selectRows : function(rows, keepExisting){
59119         if(!keepExisting){
59120             this.clearSelections();
59121         }
59122         for(var i = 0, len = rows.length; i < len; i++){
59123             this.selectRow(rows[i], true);
59124         }
59125     },
59126
59127     /**
59128      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59129      * @param {Number} startRow The index of the first row in the range
59130      * @param {Number} endRow The index of the last row in the range
59131      * @param {Boolean} keepExisting (optional) True to retain existing selections
59132      */
59133     selectRange : function(startRow, endRow, keepExisting){
59134         if(this.locked) {
59135             return;
59136         }
59137         if(!keepExisting){
59138             this.clearSelections();
59139         }
59140         if(startRow <= endRow){
59141             for(var i = startRow; i <= endRow; i++){
59142                 this.selectRow(i, true);
59143             }
59144         }else{
59145             for(var i = startRow; i >= endRow; i--){
59146                 this.selectRow(i, true);
59147             }
59148         }
59149     },
59150
59151     /**
59152      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59153      * @param {Number} startRow The index of the first row in the range
59154      * @param {Number} endRow The index of the last row in the range
59155      */
59156     deselectRange : function(startRow, endRow, preventViewNotify){
59157         if(this.locked) {
59158             return;
59159         }
59160         for(var i = startRow; i <= endRow; i++){
59161             this.deselectRow(i, preventViewNotify);
59162         }
59163     },
59164
59165     /**
59166      * Selects a row.
59167      * @param {Number} row The index of the row to select
59168      * @param {Boolean} keepExisting (optional) True to keep existing selections
59169      */
59170     selectRow : function(index, keepExisting, preventViewNotify){
59171         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
59172             return;
59173         }
59174         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59175             if(!keepExisting || this.singleSelect){
59176                 this.clearSelections();
59177             }
59178             var r = this.grid.dataSource.getAt(index);
59179             this.selections.add(r);
59180             this.last = this.lastActive = index;
59181             if(!preventViewNotify){
59182                 this.grid.getView().onRowSelect(index);
59183             }
59184             this.fireEvent("rowselect", this, index, r);
59185             this.fireEvent("selectionchange", this);
59186         }
59187     },
59188
59189     /**
59190      * Deselects a row.
59191      * @param {Number} row The index of the row to deselect
59192      */
59193     deselectRow : function(index, preventViewNotify){
59194         if(this.locked) {
59195             return;
59196         }
59197         if(this.last == index){
59198             this.last = false;
59199         }
59200         if(this.lastActive == index){
59201             this.lastActive = false;
59202         }
59203         var r = this.grid.dataSource.getAt(index);
59204         this.selections.remove(r);
59205         if(!preventViewNotify){
59206             this.grid.getView().onRowDeselect(index);
59207         }
59208         this.fireEvent("rowdeselect", this, index);
59209         this.fireEvent("selectionchange", this);
59210     },
59211
59212     // private
59213     restoreLast : function(){
59214         if(this._last){
59215             this.last = this._last;
59216         }
59217     },
59218
59219     // private
59220     acceptsNav : function(row, col, cm){
59221         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59222     },
59223
59224     // private
59225     onEditorKey : function(field, e){
59226         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59227         if(k == e.TAB){
59228             e.stopEvent();
59229             ed.completeEdit();
59230             if(e.shiftKey){
59231                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59232             }else{
59233                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59234             }
59235         }else if(k == e.ENTER && !e.ctrlKey){
59236             e.stopEvent();
59237             ed.completeEdit();
59238             if(e.shiftKey){
59239                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59240             }else{
59241                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59242             }
59243         }else if(k == e.ESC){
59244             ed.cancelEdit();
59245         }
59246         if(newCell){
59247             g.startEditing(newCell[0], newCell[1]);
59248         }
59249     }
59250 });/*
59251  * Based on:
59252  * Ext JS Library 1.1.1
59253  * Copyright(c) 2006-2007, Ext JS, LLC.
59254  *
59255  * Originally Released Under LGPL - original licence link has changed is not relivant.
59256  *
59257  * Fork - LGPL
59258  * <script type="text/javascript">
59259  */
59260 /**
59261  * @class Roo.grid.CellSelectionModel
59262  * @extends Roo.grid.AbstractSelectionModel
59263  * This class provides the basic implementation for cell selection in a grid.
59264  * @constructor
59265  * @param {Object} config The object containing the configuration of this model.
59266  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59267  */
59268 Roo.grid.CellSelectionModel = function(config){
59269     Roo.apply(this, config);
59270
59271     this.selection = null;
59272
59273     this.addEvents({
59274         /**
59275              * @event beforerowselect
59276              * Fires before a cell is selected.
59277              * @param {SelectionModel} this
59278              * @param {Number} rowIndex The selected row index
59279              * @param {Number} colIndex The selected cell index
59280              */
59281             "beforecellselect" : true,
59282         /**
59283              * @event cellselect
59284              * Fires when a cell is selected.
59285              * @param {SelectionModel} this
59286              * @param {Number} rowIndex The selected row index
59287              * @param {Number} colIndex The selected cell index
59288              */
59289             "cellselect" : true,
59290         /**
59291              * @event selectionchange
59292              * Fires when the active selection changes.
59293              * @param {SelectionModel} this
59294              * @param {Object} selection null for no selection or an object (o) with two properties
59295                 <ul>
59296                 <li>o.record: the record object for the row the selection is in</li>
59297                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59298                 </ul>
59299              */
59300             "selectionchange" : true,
59301         /**
59302              * @event tabend
59303              * Fires when the tab (or enter) was pressed on the last editable cell
59304              * You can use this to trigger add new row.
59305              * @param {SelectionModel} this
59306              */
59307             "tabend" : true,
59308          /**
59309              * @event beforeeditnext
59310              * Fires before the next editable sell is made active
59311              * You can use this to skip to another cell or fire the tabend
59312              *    if you set cell to false
59313              * @param {Object} eventdata object : { cell : [ row, col ] } 
59314              */
59315             "beforeeditnext" : true
59316     });
59317     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59318 };
59319
59320 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59321     
59322     enter_is_tab: false,
59323
59324     /** @ignore */
59325     initEvents : function(){
59326         this.grid.on("mousedown", this.handleMouseDown, this);
59327         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59328         var view = this.grid.view;
59329         view.on("refresh", this.onViewChange, this);
59330         view.on("rowupdated", this.onRowUpdated, this);
59331         view.on("beforerowremoved", this.clearSelections, this);
59332         view.on("beforerowsinserted", this.clearSelections, this);
59333         if(this.grid.isEditor){
59334             this.grid.on("beforeedit", this.beforeEdit,  this);
59335         }
59336     },
59337
59338         //private
59339     beforeEdit : function(e){
59340         this.select(e.row, e.column, false, true, e.record);
59341     },
59342
59343         //private
59344     onRowUpdated : function(v, index, r){
59345         if(this.selection && this.selection.record == r){
59346             v.onCellSelect(index, this.selection.cell[1]);
59347         }
59348     },
59349
59350         //private
59351     onViewChange : function(){
59352         this.clearSelections(true);
59353     },
59354
59355         /**
59356          * Returns the currently selected cell,.
59357          * @return {Array} The selected cell (row, column) or null if none selected.
59358          */
59359     getSelectedCell : function(){
59360         return this.selection ? this.selection.cell : null;
59361     },
59362
59363     /**
59364      * Clears all selections.
59365      * @param {Boolean} true to prevent the gridview from being notified about the change.
59366      */
59367     clearSelections : function(preventNotify){
59368         var s = this.selection;
59369         if(s){
59370             if(preventNotify !== true){
59371                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59372             }
59373             this.selection = null;
59374             this.fireEvent("selectionchange", this, null);
59375         }
59376     },
59377
59378     /**
59379      * Returns true if there is a selection.
59380      * @return {Boolean}
59381      */
59382     hasSelection : function(){
59383         return this.selection ? true : false;
59384     },
59385
59386     /** @ignore */
59387     handleMouseDown : function(e, t){
59388         var v = this.grid.getView();
59389         if(this.isLocked()){
59390             return;
59391         };
59392         var row = v.findRowIndex(t);
59393         var cell = v.findCellIndex(t);
59394         if(row !== false && cell !== false){
59395             this.select(row, cell);
59396         }
59397     },
59398
59399     /**
59400      * Selects a cell.
59401      * @param {Number} rowIndex
59402      * @param {Number} collIndex
59403      */
59404     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59405         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59406             this.clearSelections();
59407             r = r || this.grid.dataSource.getAt(rowIndex);
59408             this.selection = {
59409                 record : r,
59410                 cell : [rowIndex, colIndex]
59411             };
59412             if(!preventViewNotify){
59413                 var v = this.grid.getView();
59414                 v.onCellSelect(rowIndex, colIndex);
59415                 if(preventFocus !== true){
59416                     v.focusCell(rowIndex, colIndex);
59417                 }
59418             }
59419             this.fireEvent("cellselect", this, rowIndex, colIndex);
59420             this.fireEvent("selectionchange", this, this.selection);
59421         }
59422     },
59423
59424         //private
59425     isSelectable : function(rowIndex, colIndex, cm){
59426         return !cm.isHidden(colIndex);
59427     },
59428
59429     /** @ignore */
59430     handleKeyDown : function(e){
59431         //Roo.log('Cell Sel Model handleKeyDown');
59432         if(!e.isNavKeyPress()){
59433             return;
59434         }
59435         var g = this.grid, s = this.selection;
59436         if(!s){
59437             e.stopEvent();
59438             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59439             if(cell){
59440                 this.select(cell[0], cell[1]);
59441             }
59442             return;
59443         }
59444         var sm = this;
59445         var walk = function(row, col, step){
59446             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59447         };
59448         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59449         var newCell;
59450
59451       
59452
59453         switch(k){
59454             case e.TAB:
59455                 // handled by onEditorKey
59456                 if (g.isEditor && g.editing) {
59457                     return;
59458                 }
59459                 if(e.shiftKey) {
59460                     newCell = walk(r, c-1, -1);
59461                 } else {
59462                     newCell = walk(r, c+1, 1);
59463                 }
59464                 break;
59465             
59466             case e.DOWN:
59467                newCell = walk(r+1, c, 1);
59468                 break;
59469             
59470             case e.UP:
59471                 newCell = walk(r-1, c, -1);
59472                 break;
59473             
59474             case e.RIGHT:
59475                 newCell = walk(r, c+1, 1);
59476                 break;
59477             
59478             case e.LEFT:
59479                 newCell = walk(r, c-1, -1);
59480                 break;
59481             
59482             case e.ENTER:
59483                 
59484                 if(g.isEditor && !g.editing){
59485                    g.startEditing(r, c);
59486                    e.stopEvent();
59487                    return;
59488                 }
59489                 
59490                 
59491              break;
59492         };
59493         if(newCell){
59494             this.select(newCell[0], newCell[1]);
59495             e.stopEvent();
59496             
59497         }
59498     },
59499
59500     acceptsNav : function(row, col, cm){
59501         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59502     },
59503     /**
59504      * Selects a cell.
59505      * @param {Number} field (not used) - as it's normally used as a listener
59506      * @param {Number} e - event - fake it by using
59507      *
59508      * var e = Roo.EventObjectImpl.prototype;
59509      * e.keyCode = e.TAB
59510      *
59511      * 
59512      */
59513     onEditorKey : function(field, e){
59514         
59515         var k = e.getKey(),
59516             newCell,
59517             g = this.grid,
59518             ed = g.activeEditor,
59519             forward = false;
59520         ///Roo.log('onEditorKey' + k);
59521         
59522         
59523         if (this.enter_is_tab && k == e.ENTER) {
59524             k = e.TAB;
59525         }
59526         
59527         if(k == e.TAB){
59528             if(e.shiftKey){
59529                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59530             }else{
59531                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59532                 forward = true;
59533             }
59534             
59535             e.stopEvent();
59536             
59537         } else if(k == e.ENTER &&  !e.ctrlKey){
59538             ed.completeEdit();
59539             e.stopEvent();
59540             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59541         
59542                 } else if(k == e.ESC){
59543             ed.cancelEdit();
59544         }
59545                 
59546         if (newCell) {
59547             var ecall = { cell : newCell, forward : forward };
59548             this.fireEvent('beforeeditnext', ecall );
59549             newCell = ecall.cell;
59550                         forward = ecall.forward;
59551         }
59552                 
59553         if(newCell){
59554             //Roo.log('next cell after edit');
59555             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59556         } else if (forward) {
59557             // tabbed past last
59558             this.fireEvent.defer(100, this, ['tabend',this]);
59559         }
59560     }
59561 });/*
59562  * Based on:
59563  * Ext JS Library 1.1.1
59564  * Copyright(c) 2006-2007, Ext JS, LLC.
59565  *
59566  * Originally Released Under LGPL - original licence link has changed is not relivant.
59567  *
59568  * Fork - LGPL
59569  * <script type="text/javascript">
59570  */
59571  
59572 /**
59573  * @class Roo.grid.EditorGrid
59574  * @extends Roo.grid.Grid
59575  * Class for creating and editable grid.
59576  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59577  * The container MUST have some type of size defined for the grid to fill. The container will be 
59578  * automatically set to position relative if it isn't already.
59579  * @param {Object} dataSource The data model to bind to
59580  * @param {Object} colModel The column model with info about this grid's columns
59581  */
59582 Roo.grid.EditorGrid = function(container, config){
59583     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59584     this.getGridEl().addClass("xedit-grid");
59585
59586     if(!this.selModel){
59587         this.selModel = new Roo.grid.CellSelectionModel();
59588     }
59589
59590     this.activeEditor = null;
59591
59592         this.addEvents({
59593             /**
59594              * @event beforeedit
59595              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59596              * <ul style="padding:5px;padding-left:16px;">
59597              * <li>grid - This grid</li>
59598              * <li>record - The record being edited</li>
59599              * <li>field - The field name being edited</li>
59600              * <li>value - The value for the field being edited.</li>
59601              * <li>row - The grid row index</li>
59602              * <li>column - The grid column index</li>
59603              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59604              * </ul>
59605              * @param {Object} e An edit event (see above for description)
59606              */
59607             "beforeedit" : true,
59608             /**
59609              * @event afteredit
59610              * Fires after a cell is edited. <br />
59611              * <ul style="padding:5px;padding-left:16px;">
59612              * <li>grid - This grid</li>
59613              * <li>record - The record being edited</li>
59614              * <li>field - The field name being edited</li>
59615              * <li>value - The value being set</li>
59616              * <li>originalValue - The original value for the field, before the edit.</li>
59617              * <li>row - The grid row index</li>
59618              * <li>column - The grid column index</li>
59619              * </ul>
59620              * @param {Object} e An edit event (see above for description)
59621              */
59622             "afteredit" : true,
59623             /**
59624              * @event validateedit
59625              * Fires after a cell is edited, but before the value is set in the record. 
59626          * You can use this to modify the value being set in the field, Return false
59627              * to cancel the change. The edit event object has the following properties <br />
59628              * <ul style="padding:5px;padding-left:16px;">
59629          * <li>editor - This editor</li>
59630              * <li>grid - This grid</li>
59631              * <li>record - The record being edited</li>
59632              * <li>field - The field name being edited</li>
59633              * <li>value - The value being set</li>
59634              * <li>originalValue - The original value for the field, before the edit.</li>
59635              * <li>row - The grid row index</li>
59636              * <li>column - The grid column index</li>
59637              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59638              * </ul>
59639              * @param {Object} e An edit event (see above for description)
59640              */
59641             "validateedit" : true
59642         });
59643     this.on("bodyscroll", this.stopEditing,  this);
59644     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59645 };
59646
59647 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59648     /**
59649      * @cfg {Number} clicksToEdit
59650      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59651      */
59652     clicksToEdit: 2,
59653
59654     // private
59655     isEditor : true,
59656     // private
59657     trackMouseOver: false, // causes very odd FF errors
59658
59659     onCellDblClick : function(g, row, col){
59660         this.startEditing(row, col);
59661     },
59662
59663     onEditComplete : function(ed, value, startValue){
59664         this.editing = false;
59665         this.activeEditor = null;
59666         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59667         var r = ed.record;
59668         var field = this.colModel.getDataIndex(ed.col);
59669         var e = {
59670             grid: this,
59671             record: r,
59672             field: field,
59673             originalValue: startValue,
59674             value: value,
59675             row: ed.row,
59676             column: ed.col,
59677             cancel:false,
59678             editor: ed
59679         };
59680         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59681         cell.show();
59682           
59683         if(String(value) !== String(startValue)){
59684             
59685             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59686                 r.set(field, e.value);
59687                 // if we are dealing with a combo box..
59688                 // then we also set the 'name' colum to be the displayField
59689                 if (ed.field.displayField && ed.field.name) {
59690                     r.set(ed.field.name, ed.field.el.dom.value);
59691                 }
59692                 
59693                 delete e.cancel; //?? why!!!
59694                 this.fireEvent("afteredit", e);
59695             }
59696         } else {
59697             this.fireEvent("afteredit", e); // always fire it!
59698         }
59699         this.view.focusCell(ed.row, ed.col);
59700     },
59701
59702     /**
59703      * Starts editing the specified for the specified row/column
59704      * @param {Number} rowIndex
59705      * @param {Number} colIndex
59706      */
59707     startEditing : function(row, col){
59708         this.stopEditing();
59709         if(this.colModel.isCellEditable(col, row)){
59710             this.view.ensureVisible(row, col, true);
59711           
59712             var r = this.dataSource.getAt(row);
59713             var field = this.colModel.getDataIndex(col);
59714             var cell = Roo.get(this.view.getCell(row,col));
59715             var e = {
59716                 grid: this,
59717                 record: r,
59718                 field: field,
59719                 value: r.data[field],
59720                 row: row,
59721                 column: col,
59722                 cancel:false 
59723             };
59724             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59725                 this.editing = true;
59726                 var ed = this.colModel.getCellEditor(col, row);
59727                 
59728                 if (!ed) {
59729                     return;
59730                 }
59731                 if(!ed.rendered){
59732                     ed.render(ed.parentEl || document.body);
59733                 }
59734                 ed.field.reset();
59735                
59736                 cell.hide();
59737                 
59738                 (function(){ // complex but required for focus issues in safari, ie and opera
59739                     ed.row = row;
59740                     ed.col = col;
59741                     ed.record = r;
59742                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59743                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59744                     this.activeEditor = ed;
59745                     var v = r.data[field];
59746                     ed.startEdit(this.view.getCell(row, col), v);
59747                     // combo's with 'displayField and name set
59748                     if (ed.field.displayField && ed.field.name) {
59749                         ed.field.el.dom.value = r.data[ed.field.name];
59750                     }
59751                     
59752                     
59753                 }).defer(50, this);
59754             }
59755         }
59756     },
59757         
59758     /**
59759      * Stops any active editing
59760      */
59761     stopEditing : function(){
59762         if(this.activeEditor){
59763             this.activeEditor.completeEdit();
59764         }
59765         this.activeEditor = null;
59766     },
59767         
59768          /**
59769      * Called to get grid's drag proxy text, by default returns this.ddText.
59770      * @return {String}
59771      */
59772     getDragDropText : function(){
59773         var count = this.selModel.getSelectedCell() ? 1 : 0;
59774         return String.format(this.ddText, count, count == 1 ? '' : 's');
59775     }
59776         
59777 });/*
59778  * Based on:
59779  * Ext JS Library 1.1.1
59780  * Copyright(c) 2006-2007, Ext JS, LLC.
59781  *
59782  * Originally Released Under LGPL - original licence link has changed is not relivant.
59783  *
59784  * Fork - LGPL
59785  * <script type="text/javascript">
59786  */
59787
59788 // private - not really -- you end up using it !
59789 // This is a support class used internally by the Grid components
59790
59791 /**
59792  * @class Roo.grid.GridEditor
59793  * @extends Roo.Editor
59794  * Class for creating and editable grid elements.
59795  * @param {Object} config any settings (must include field)
59796  */
59797 Roo.grid.GridEditor = function(field, config){
59798     if (!config && field.field) {
59799         config = field;
59800         field = Roo.factory(config.field, Roo.form);
59801     }
59802     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59803     field.monitorTab = false;
59804 };
59805
59806 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59807     
59808     /**
59809      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59810      */
59811     
59812     alignment: "tl-tl",
59813     autoSize: "width",
59814     hideEl : false,
59815     cls: "x-small-editor x-grid-editor",
59816     shim:false,
59817     shadow:"frame"
59818 });/*
59819  * Based on:
59820  * Ext JS Library 1.1.1
59821  * Copyright(c) 2006-2007, Ext JS, LLC.
59822  *
59823  * Originally Released Under LGPL - original licence link has changed is not relivant.
59824  *
59825  * Fork - LGPL
59826  * <script type="text/javascript">
59827  */
59828   
59829
59830   
59831 Roo.grid.PropertyRecord = Roo.data.Record.create([
59832     {name:'name',type:'string'},  'value'
59833 ]);
59834
59835
59836 Roo.grid.PropertyStore = function(grid, source){
59837     this.grid = grid;
59838     this.store = new Roo.data.Store({
59839         recordType : Roo.grid.PropertyRecord
59840     });
59841     this.store.on('update', this.onUpdate,  this);
59842     if(source){
59843         this.setSource(source);
59844     }
59845     Roo.grid.PropertyStore.superclass.constructor.call(this);
59846 };
59847
59848
59849
59850 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
59851     setSource : function(o){
59852         this.source = o;
59853         this.store.removeAll();
59854         var data = [];
59855         for(var k in o){
59856             if(this.isEditableValue(o[k])){
59857                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
59858             }
59859         }
59860         this.store.loadRecords({records: data}, {}, true);
59861     },
59862
59863     onUpdate : function(ds, record, type){
59864         if(type == Roo.data.Record.EDIT){
59865             var v = record.data['value'];
59866             var oldValue = record.modified['value'];
59867             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
59868                 this.source[record.id] = v;
59869                 record.commit();
59870                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
59871             }else{
59872                 record.reject();
59873             }
59874         }
59875     },
59876
59877     getProperty : function(row){
59878        return this.store.getAt(row);
59879     },
59880
59881     isEditableValue: function(val){
59882         if(val && val instanceof Date){
59883             return true;
59884         }else if(typeof val == 'object' || typeof val == 'function'){
59885             return false;
59886         }
59887         return true;
59888     },
59889
59890     setValue : function(prop, value){
59891         this.source[prop] = value;
59892         this.store.getById(prop).set('value', value);
59893     },
59894
59895     getSource : function(){
59896         return this.source;
59897     }
59898 });
59899
59900 Roo.grid.PropertyColumnModel = function(grid, store){
59901     this.grid = grid;
59902     var g = Roo.grid;
59903     g.PropertyColumnModel.superclass.constructor.call(this, [
59904         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
59905         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
59906     ]);
59907     this.store = store;
59908     this.bselect = Roo.DomHelper.append(document.body, {
59909         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
59910             {tag: 'option', value: 'true', html: 'true'},
59911             {tag: 'option', value: 'false', html: 'false'}
59912         ]
59913     });
59914     Roo.id(this.bselect);
59915     var f = Roo.form;
59916     this.editors = {
59917         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
59918         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
59919         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
59920         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
59921         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
59922     };
59923     this.renderCellDelegate = this.renderCell.createDelegate(this);
59924     this.renderPropDelegate = this.renderProp.createDelegate(this);
59925 };
59926
59927 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
59928     
59929     
59930     nameText : 'Name',
59931     valueText : 'Value',
59932     
59933     dateFormat : 'm/j/Y',
59934     
59935     
59936     renderDate : function(dateVal){
59937         return dateVal.dateFormat(this.dateFormat);
59938     },
59939
59940     renderBool : function(bVal){
59941         return bVal ? 'true' : 'false';
59942     },
59943
59944     isCellEditable : function(colIndex, rowIndex){
59945         return colIndex == 1;
59946     },
59947
59948     getRenderer : function(col){
59949         return col == 1 ?
59950             this.renderCellDelegate : this.renderPropDelegate;
59951     },
59952
59953     renderProp : function(v){
59954         return this.getPropertyName(v);
59955     },
59956
59957     renderCell : function(val){
59958         var rv = val;
59959         if(val instanceof Date){
59960             rv = this.renderDate(val);
59961         }else if(typeof val == 'boolean'){
59962             rv = this.renderBool(val);
59963         }
59964         return Roo.util.Format.htmlEncode(rv);
59965     },
59966
59967     getPropertyName : function(name){
59968         var pn = this.grid.propertyNames;
59969         return pn && pn[name] ? pn[name] : name;
59970     },
59971
59972     getCellEditor : function(colIndex, rowIndex){
59973         var p = this.store.getProperty(rowIndex);
59974         var n = p.data['name'], val = p.data['value'];
59975         
59976         if(typeof(this.grid.customEditors[n]) == 'string'){
59977             return this.editors[this.grid.customEditors[n]];
59978         }
59979         if(typeof(this.grid.customEditors[n]) != 'undefined'){
59980             return this.grid.customEditors[n];
59981         }
59982         if(val instanceof Date){
59983             return this.editors['date'];
59984         }else if(typeof val == 'number'){
59985             return this.editors['number'];
59986         }else if(typeof val == 'boolean'){
59987             return this.editors['boolean'];
59988         }else{
59989             return this.editors['string'];
59990         }
59991     }
59992 });
59993
59994 /**
59995  * @class Roo.grid.PropertyGrid
59996  * @extends Roo.grid.EditorGrid
59997  * This class represents the  interface of a component based property grid control.
59998  * <br><br>Usage:<pre><code>
59999  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60000       
60001  });
60002  // set any options
60003  grid.render();
60004  * </code></pre>
60005   
60006  * @constructor
60007  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60008  * The container MUST have some type of size defined for the grid to fill. The container will be
60009  * automatically set to position relative if it isn't already.
60010  * @param {Object} config A config object that sets properties on this grid.
60011  */
60012 Roo.grid.PropertyGrid = function(container, config){
60013     config = config || {};
60014     var store = new Roo.grid.PropertyStore(this);
60015     this.store = store;
60016     var cm = new Roo.grid.PropertyColumnModel(this, store);
60017     store.store.sort('name', 'ASC');
60018     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60019         ds: store.store,
60020         cm: cm,
60021         enableColLock:false,
60022         enableColumnMove:false,
60023         stripeRows:false,
60024         trackMouseOver: false,
60025         clicksToEdit:1
60026     }, config));
60027     this.getGridEl().addClass('x-props-grid');
60028     this.lastEditRow = null;
60029     this.on('columnresize', this.onColumnResize, this);
60030     this.addEvents({
60031          /**
60032              * @event beforepropertychange
60033              * Fires before a property changes (return false to stop?)
60034              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60035              * @param {String} id Record Id
60036              * @param {String} newval New Value
60037          * @param {String} oldval Old Value
60038              */
60039         "beforepropertychange": true,
60040         /**
60041              * @event propertychange
60042              * Fires after a property changes
60043              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60044              * @param {String} id Record Id
60045              * @param {String} newval New Value
60046          * @param {String} oldval Old Value
60047              */
60048         "propertychange": true
60049     });
60050     this.customEditors = this.customEditors || {};
60051 };
60052 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60053     
60054      /**
60055      * @cfg {Object} customEditors map of colnames=> custom editors.
60056      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60057      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60058      * false disables editing of the field.
60059          */
60060     
60061       /**
60062      * @cfg {Object} propertyNames map of property Names to their displayed value
60063          */
60064     
60065     render : function(){
60066         Roo.grid.PropertyGrid.superclass.render.call(this);
60067         this.autoSize.defer(100, this);
60068     },
60069
60070     autoSize : function(){
60071         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60072         if(this.view){
60073             this.view.fitColumns();
60074         }
60075     },
60076
60077     onColumnResize : function(){
60078         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60079         this.autoSize();
60080     },
60081     /**
60082      * Sets the data for the Grid
60083      * accepts a Key => Value object of all the elements avaiable.
60084      * @param {Object} data  to appear in grid.
60085      */
60086     setSource : function(source){
60087         this.store.setSource(source);
60088         //this.autoSize();
60089     },
60090     /**
60091      * Gets all the data from the grid.
60092      * @return {Object} data  data stored in grid
60093      */
60094     getSource : function(){
60095         return this.store.getSource();
60096     }
60097 });/*
60098   
60099  * Licence LGPL
60100  
60101  */
60102  
60103 /**
60104  * @class Roo.grid.Calendar
60105  * @extends Roo.util.Grid
60106  * This class extends the Grid to provide a calendar widget
60107  * <br><br>Usage:<pre><code>
60108  var grid = new Roo.grid.Calendar("my-container-id", {
60109      ds: myDataStore,
60110      cm: myColModel,
60111      selModel: mySelectionModel,
60112      autoSizeColumns: true,
60113      monitorWindowResize: false,
60114      trackMouseOver: true
60115      eventstore : real data store..
60116  });
60117  // set any options
60118  grid.render();
60119   
60120   * @constructor
60121  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60122  * The container MUST have some type of size defined for the grid to fill. The container will be
60123  * automatically set to position relative if it isn't already.
60124  * @param {Object} config A config object that sets properties on this grid.
60125  */
60126 Roo.grid.Calendar = function(container, config){
60127         // initialize the container
60128         this.container = Roo.get(container);
60129         this.container.update("");
60130         this.container.setStyle("overflow", "hidden");
60131     this.container.addClass('x-grid-container');
60132
60133     this.id = this.container.id;
60134
60135     Roo.apply(this, config);
60136     // check and correct shorthanded configs
60137     
60138     var rows = [];
60139     var d =1;
60140     for (var r = 0;r < 6;r++) {
60141         
60142         rows[r]=[];
60143         for (var c =0;c < 7;c++) {
60144             rows[r][c]= '';
60145         }
60146     }
60147     if (this.eventStore) {
60148         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60149         this.eventStore.on('load',this.onLoad, this);
60150         this.eventStore.on('beforeload',this.clearEvents, this);
60151          
60152     }
60153     
60154     this.dataSource = new Roo.data.Store({
60155             proxy: new Roo.data.MemoryProxy(rows),
60156             reader: new Roo.data.ArrayReader({}, [
60157                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60158     });
60159
60160     this.dataSource.load();
60161     this.ds = this.dataSource;
60162     this.ds.xmodule = this.xmodule || false;
60163     
60164     
60165     var cellRender = function(v,x,r)
60166     {
60167         return String.format(
60168             '<div class="fc-day  fc-widget-content"><div>' +
60169                 '<div class="fc-event-container"></div>' +
60170                 '<div class="fc-day-number">{0}</div>'+
60171                 
60172                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60173             '</div></div>', v);
60174     
60175     }
60176     
60177     
60178     this.colModel = new Roo.grid.ColumnModel( [
60179         {
60180             xtype: 'ColumnModel',
60181             xns: Roo.grid,
60182             dataIndex : 'weekday0',
60183             header : 'Sunday',
60184             renderer : cellRender
60185         },
60186         {
60187             xtype: 'ColumnModel',
60188             xns: Roo.grid,
60189             dataIndex : 'weekday1',
60190             header : 'Monday',
60191             renderer : cellRender
60192         },
60193         {
60194             xtype: 'ColumnModel',
60195             xns: Roo.grid,
60196             dataIndex : 'weekday2',
60197             header : 'Tuesday',
60198             renderer : cellRender
60199         },
60200         {
60201             xtype: 'ColumnModel',
60202             xns: Roo.grid,
60203             dataIndex : 'weekday3',
60204             header : 'Wednesday',
60205             renderer : cellRender
60206         },
60207         {
60208             xtype: 'ColumnModel',
60209             xns: Roo.grid,
60210             dataIndex : 'weekday4',
60211             header : 'Thursday',
60212             renderer : cellRender
60213         },
60214         {
60215             xtype: 'ColumnModel',
60216             xns: Roo.grid,
60217             dataIndex : 'weekday5',
60218             header : 'Friday',
60219             renderer : cellRender
60220         },
60221         {
60222             xtype: 'ColumnModel',
60223             xns: Roo.grid,
60224             dataIndex : 'weekday6',
60225             header : 'Saturday',
60226             renderer : cellRender
60227         }
60228     ]);
60229     this.cm = this.colModel;
60230     this.cm.xmodule = this.xmodule || false;
60231  
60232         
60233           
60234     //this.selModel = new Roo.grid.CellSelectionModel();
60235     //this.sm = this.selModel;
60236     //this.selModel.init(this);
60237     
60238     
60239     if(this.width){
60240         this.container.setWidth(this.width);
60241     }
60242
60243     if(this.height){
60244         this.container.setHeight(this.height);
60245     }
60246     /** @private */
60247         this.addEvents({
60248         // raw events
60249         /**
60250          * @event click
60251          * The raw click event for the entire grid.
60252          * @param {Roo.EventObject} e
60253          */
60254         "click" : true,
60255         /**
60256          * @event dblclick
60257          * The raw dblclick event for the entire grid.
60258          * @param {Roo.EventObject} e
60259          */
60260         "dblclick" : true,
60261         /**
60262          * @event contextmenu
60263          * The raw contextmenu event for the entire grid.
60264          * @param {Roo.EventObject} e
60265          */
60266         "contextmenu" : true,
60267         /**
60268          * @event mousedown
60269          * The raw mousedown event for the entire grid.
60270          * @param {Roo.EventObject} e
60271          */
60272         "mousedown" : true,
60273         /**
60274          * @event mouseup
60275          * The raw mouseup event for the entire grid.
60276          * @param {Roo.EventObject} e
60277          */
60278         "mouseup" : true,
60279         /**
60280          * @event mouseover
60281          * The raw mouseover event for the entire grid.
60282          * @param {Roo.EventObject} e
60283          */
60284         "mouseover" : true,
60285         /**
60286          * @event mouseout
60287          * The raw mouseout event for the entire grid.
60288          * @param {Roo.EventObject} e
60289          */
60290         "mouseout" : true,
60291         /**
60292          * @event keypress
60293          * The raw keypress event for the entire grid.
60294          * @param {Roo.EventObject} e
60295          */
60296         "keypress" : true,
60297         /**
60298          * @event keydown
60299          * The raw keydown event for the entire grid.
60300          * @param {Roo.EventObject} e
60301          */
60302         "keydown" : true,
60303
60304         // custom events
60305
60306         /**
60307          * @event cellclick
60308          * Fires when a cell is clicked
60309          * @param {Grid} this
60310          * @param {Number} rowIndex
60311          * @param {Number} columnIndex
60312          * @param {Roo.EventObject} e
60313          */
60314         "cellclick" : true,
60315         /**
60316          * @event celldblclick
60317          * Fires when a cell is double clicked
60318          * @param {Grid} this
60319          * @param {Number} rowIndex
60320          * @param {Number} columnIndex
60321          * @param {Roo.EventObject} e
60322          */
60323         "celldblclick" : true,
60324         /**
60325          * @event rowclick
60326          * Fires when a row is clicked
60327          * @param {Grid} this
60328          * @param {Number} rowIndex
60329          * @param {Roo.EventObject} e
60330          */
60331         "rowclick" : true,
60332         /**
60333          * @event rowdblclick
60334          * Fires when a row is double clicked
60335          * @param {Grid} this
60336          * @param {Number} rowIndex
60337          * @param {Roo.EventObject} e
60338          */
60339         "rowdblclick" : true,
60340         /**
60341          * @event headerclick
60342          * Fires when a header is clicked
60343          * @param {Grid} this
60344          * @param {Number} columnIndex
60345          * @param {Roo.EventObject} e
60346          */
60347         "headerclick" : true,
60348         /**
60349          * @event headerdblclick
60350          * Fires when a header cell is double clicked
60351          * @param {Grid} this
60352          * @param {Number} columnIndex
60353          * @param {Roo.EventObject} e
60354          */
60355         "headerdblclick" : true,
60356         /**
60357          * @event rowcontextmenu
60358          * Fires when a row is right clicked
60359          * @param {Grid} this
60360          * @param {Number} rowIndex
60361          * @param {Roo.EventObject} e
60362          */
60363         "rowcontextmenu" : true,
60364         /**
60365          * @event cellcontextmenu
60366          * Fires when a cell is right clicked
60367          * @param {Grid} this
60368          * @param {Number} rowIndex
60369          * @param {Number} cellIndex
60370          * @param {Roo.EventObject} e
60371          */
60372          "cellcontextmenu" : true,
60373         /**
60374          * @event headercontextmenu
60375          * Fires when a header is right clicked
60376          * @param {Grid} this
60377          * @param {Number} columnIndex
60378          * @param {Roo.EventObject} e
60379          */
60380         "headercontextmenu" : true,
60381         /**
60382          * @event bodyscroll
60383          * Fires when the body element is scrolled
60384          * @param {Number} scrollLeft
60385          * @param {Number} scrollTop
60386          */
60387         "bodyscroll" : true,
60388         /**
60389          * @event columnresize
60390          * Fires when the user resizes a column
60391          * @param {Number} columnIndex
60392          * @param {Number} newSize
60393          */
60394         "columnresize" : true,
60395         /**
60396          * @event columnmove
60397          * Fires when the user moves a column
60398          * @param {Number} oldIndex
60399          * @param {Number} newIndex
60400          */
60401         "columnmove" : true,
60402         /**
60403          * @event startdrag
60404          * Fires when row(s) start being dragged
60405          * @param {Grid} this
60406          * @param {Roo.GridDD} dd The drag drop object
60407          * @param {event} e The raw browser event
60408          */
60409         "startdrag" : true,
60410         /**
60411          * @event enddrag
60412          * Fires when a drag operation is complete
60413          * @param {Grid} this
60414          * @param {Roo.GridDD} dd The drag drop object
60415          * @param {event} e The raw browser event
60416          */
60417         "enddrag" : true,
60418         /**
60419          * @event dragdrop
60420          * Fires when dragged row(s) are dropped on a valid DD target
60421          * @param {Grid} this
60422          * @param {Roo.GridDD} dd The drag drop object
60423          * @param {String} targetId The target drag drop object
60424          * @param {event} e The raw browser event
60425          */
60426         "dragdrop" : true,
60427         /**
60428          * @event dragover
60429          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60430          * @param {Grid} this
60431          * @param {Roo.GridDD} dd The drag drop object
60432          * @param {String} targetId The target drag drop object
60433          * @param {event} e The raw browser event
60434          */
60435         "dragover" : true,
60436         /**
60437          * @event dragenter
60438          *  Fires when the dragged row(s) first cross another DD target while being dragged
60439          * @param {Grid} this
60440          * @param {Roo.GridDD} dd The drag drop object
60441          * @param {String} targetId The target drag drop object
60442          * @param {event} e The raw browser event
60443          */
60444         "dragenter" : true,
60445         /**
60446          * @event dragout
60447          * Fires when the dragged row(s) leave another DD target while being dragged
60448          * @param {Grid} this
60449          * @param {Roo.GridDD} dd The drag drop object
60450          * @param {String} targetId The target drag drop object
60451          * @param {event} e The raw browser event
60452          */
60453         "dragout" : true,
60454         /**
60455          * @event rowclass
60456          * Fires when a row is rendered, so you can change add a style to it.
60457          * @param {GridView} gridview   The grid view
60458          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60459          */
60460         'rowclass' : true,
60461
60462         /**
60463          * @event render
60464          * Fires when the grid is rendered
60465          * @param {Grid} grid
60466          */
60467         'render' : true,
60468             /**
60469              * @event select
60470              * Fires when a date is selected
60471              * @param {DatePicker} this
60472              * @param {Date} date The selected date
60473              */
60474         'select': true,
60475         /**
60476              * @event monthchange
60477              * Fires when the displayed month changes 
60478              * @param {DatePicker} this
60479              * @param {Date} date The selected month
60480              */
60481         'monthchange': true,
60482         /**
60483              * @event evententer
60484              * Fires when mouse over an event
60485              * @param {Calendar} this
60486              * @param {event} Event
60487              */
60488         'evententer': true,
60489         /**
60490              * @event eventleave
60491              * Fires when the mouse leaves an
60492              * @param {Calendar} this
60493              * @param {event}
60494              */
60495         'eventleave': true,
60496         /**
60497              * @event eventclick
60498              * Fires when the mouse click an
60499              * @param {Calendar} this
60500              * @param {event}
60501              */
60502         'eventclick': true,
60503         /**
60504              * @event eventrender
60505              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60506              * @param {Calendar} this
60507              * @param {data} data to be modified
60508              */
60509         'eventrender': true
60510         
60511     });
60512
60513     Roo.grid.Grid.superclass.constructor.call(this);
60514     this.on('render', function() {
60515         this.view.el.addClass('x-grid-cal'); 
60516         
60517         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60518
60519     },this);
60520     
60521     if (!Roo.grid.Calendar.style) {
60522         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60523             
60524             
60525             '.x-grid-cal .x-grid-col' :  {
60526                 height: 'auto !important',
60527                 'vertical-align': 'top'
60528             },
60529             '.x-grid-cal  .fc-event-hori' : {
60530                 height: '14px'
60531             }
60532              
60533             
60534         }, Roo.id());
60535     }
60536
60537     
60538     
60539 };
60540 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60541     /**
60542      * @cfg {Store} eventStore The store that loads events.
60543      */
60544     eventStore : 25,
60545
60546      
60547     activeDate : false,
60548     startDay : 0,
60549     autoWidth : true,
60550     monitorWindowResize : false,
60551
60552     
60553     resizeColumns : function() {
60554         var col = (this.view.el.getWidth() / 7) - 3;
60555         // loop through cols, and setWidth
60556         for(var i =0 ; i < 7 ; i++){
60557             this.cm.setColumnWidth(i, col);
60558         }
60559     },
60560      setDate :function(date) {
60561         
60562         Roo.log('setDate?');
60563         
60564         this.resizeColumns();
60565         var vd = this.activeDate;
60566         this.activeDate = date;
60567 //        if(vd && this.el){
60568 //            var t = date.getTime();
60569 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60570 //                Roo.log('using add remove');
60571 //                
60572 //                this.fireEvent('monthchange', this, date);
60573 //                
60574 //                this.cells.removeClass("fc-state-highlight");
60575 //                this.cells.each(function(c){
60576 //                   if(c.dateValue == t){
60577 //                       c.addClass("fc-state-highlight");
60578 //                       setTimeout(function(){
60579 //                            try{c.dom.firstChild.focus();}catch(e){}
60580 //                       }, 50);
60581 //                       return false;
60582 //                   }
60583 //                   return true;
60584 //                });
60585 //                return;
60586 //            }
60587 //        }
60588         
60589         var days = date.getDaysInMonth();
60590         
60591         var firstOfMonth = date.getFirstDateOfMonth();
60592         var startingPos = firstOfMonth.getDay()-this.startDay;
60593         
60594         if(startingPos < this.startDay){
60595             startingPos += 7;
60596         }
60597         
60598         var pm = date.add(Date.MONTH, -1);
60599         var prevStart = pm.getDaysInMonth()-startingPos;
60600 //        
60601         
60602         
60603         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60604         
60605         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60606         //this.cells.addClassOnOver('fc-state-hover');
60607         
60608         var cells = this.cells.elements;
60609         var textEls = this.textNodes;
60610         
60611         //Roo.each(cells, function(cell){
60612         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60613         //});
60614         
60615         days += startingPos;
60616
60617         // convert everything to numbers so it's fast
60618         var day = 86400000;
60619         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60620         //Roo.log(d);
60621         //Roo.log(pm);
60622         //Roo.log(prevStart);
60623         
60624         var today = new Date().clearTime().getTime();
60625         var sel = date.clearTime().getTime();
60626         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60627         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60628         var ddMatch = this.disabledDatesRE;
60629         var ddText = this.disabledDatesText;
60630         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60631         var ddaysText = this.disabledDaysText;
60632         var format = this.format;
60633         
60634         var setCellClass = function(cal, cell){
60635             
60636             //Roo.log('set Cell Class');
60637             cell.title = "";
60638             var t = d.getTime();
60639             
60640             //Roo.log(d);
60641             
60642             
60643             cell.dateValue = t;
60644             if(t == today){
60645                 cell.className += " fc-today";
60646                 cell.className += " fc-state-highlight";
60647                 cell.title = cal.todayText;
60648             }
60649             if(t == sel){
60650                 // disable highlight in other month..
60651                 cell.className += " fc-state-highlight";
60652                 
60653             }
60654             // disabling
60655             if(t < min) {
60656                 //cell.className = " fc-state-disabled";
60657                 cell.title = cal.minText;
60658                 return;
60659             }
60660             if(t > max) {
60661                 //cell.className = " fc-state-disabled";
60662                 cell.title = cal.maxText;
60663                 return;
60664             }
60665             if(ddays){
60666                 if(ddays.indexOf(d.getDay()) != -1){
60667                     // cell.title = ddaysText;
60668                    // cell.className = " fc-state-disabled";
60669                 }
60670             }
60671             if(ddMatch && format){
60672                 var fvalue = d.dateFormat(format);
60673                 if(ddMatch.test(fvalue)){
60674                     cell.title = ddText.replace("%0", fvalue);
60675                    cell.className = " fc-state-disabled";
60676                 }
60677             }
60678             
60679             if (!cell.initialClassName) {
60680                 cell.initialClassName = cell.dom.className;
60681             }
60682             
60683             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60684         };
60685
60686         var i = 0;
60687         
60688         for(; i < startingPos; i++) {
60689             cells[i].dayName =  (++prevStart);
60690             Roo.log(textEls[i]);
60691             d.setDate(d.getDate()+1);
60692             
60693             //cells[i].className = "fc-past fc-other-month";
60694             setCellClass(this, cells[i]);
60695         }
60696         
60697         var intDay = 0;
60698         
60699         for(; i < days; i++){
60700             intDay = i - startingPos + 1;
60701             cells[i].dayName =  (intDay);
60702             d.setDate(d.getDate()+1);
60703             
60704             cells[i].className = ''; // "x-date-active";
60705             setCellClass(this, cells[i]);
60706         }
60707         var extraDays = 0;
60708         
60709         for(; i < 42; i++) {
60710             //textEls[i].innerHTML = (++extraDays);
60711             
60712             d.setDate(d.getDate()+1);
60713             cells[i].dayName = (++extraDays);
60714             cells[i].className = "fc-future fc-other-month";
60715             setCellClass(this, cells[i]);
60716         }
60717         
60718         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60719         
60720         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60721         
60722         // this will cause all the cells to mis
60723         var rows= [];
60724         var i =0;
60725         for (var r = 0;r < 6;r++) {
60726             for (var c =0;c < 7;c++) {
60727                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60728             }    
60729         }
60730         
60731         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60732         for(i=0;i<cells.length;i++) {
60733             
60734             this.cells.elements[i].dayName = cells[i].dayName ;
60735             this.cells.elements[i].className = cells[i].className;
60736             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60737             this.cells.elements[i].title = cells[i].title ;
60738             this.cells.elements[i].dateValue = cells[i].dateValue ;
60739         }
60740         
60741         
60742         
60743         
60744         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60745         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60746         
60747         ////if(totalRows != 6){
60748             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60749            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60750        // }
60751         
60752         this.fireEvent('monthchange', this, date);
60753         
60754         
60755     },
60756  /**
60757      * Returns the grid's SelectionModel.
60758      * @return {SelectionModel}
60759      */
60760     getSelectionModel : function(){
60761         if(!this.selModel){
60762             this.selModel = new Roo.grid.CellSelectionModel();
60763         }
60764         return this.selModel;
60765     },
60766
60767     load: function() {
60768         this.eventStore.load()
60769         
60770         
60771         
60772     },
60773     
60774     findCell : function(dt) {
60775         dt = dt.clearTime().getTime();
60776         var ret = false;
60777         this.cells.each(function(c){
60778             //Roo.log("check " +c.dateValue + '?=' + dt);
60779             if(c.dateValue == dt){
60780                 ret = c;
60781                 return false;
60782             }
60783             return true;
60784         });
60785         
60786         return ret;
60787     },
60788     
60789     findCells : function(rec) {
60790         var s = rec.data.start_dt.clone().clearTime().getTime();
60791        // Roo.log(s);
60792         var e= rec.data.end_dt.clone().clearTime().getTime();
60793        // Roo.log(e);
60794         var ret = [];
60795         this.cells.each(function(c){
60796              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60797             
60798             if(c.dateValue > e){
60799                 return ;
60800             }
60801             if(c.dateValue < s){
60802                 return ;
60803             }
60804             ret.push(c);
60805         });
60806         
60807         return ret;    
60808     },
60809     
60810     findBestRow: function(cells)
60811     {
60812         var ret = 0;
60813         
60814         for (var i =0 ; i < cells.length;i++) {
60815             ret  = Math.max(cells[i].rows || 0,ret);
60816         }
60817         return ret;
60818         
60819     },
60820     
60821     
60822     addItem : function(rec)
60823     {
60824         // look for vertical location slot in
60825         var cells = this.findCells(rec);
60826         
60827         rec.row = this.findBestRow(cells);
60828         
60829         // work out the location.
60830         
60831         var crow = false;
60832         var rows = [];
60833         for(var i =0; i < cells.length; i++) {
60834             if (!crow) {
60835                 crow = {
60836                     start : cells[i],
60837                     end :  cells[i]
60838                 };
60839                 continue;
60840             }
60841             if (crow.start.getY() == cells[i].getY()) {
60842                 // on same row.
60843                 crow.end = cells[i];
60844                 continue;
60845             }
60846             // different row.
60847             rows.push(crow);
60848             crow = {
60849                 start: cells[i],
60850                 end : cells[i]
60851             };
60852             
60853         }
60854         
60855         rows.push(crow);
60856         rec.els = [];
60857         rec.rows = rows;
60858         rec.cells = cells;
60859         for (var i = 0; i < cells.length;i++) {
60860             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
60861             
60862         }
60863         
60864         
60865     },
60866     
60867     clearEvents: function() {
60868         
60869         if (!this.eventStore.getCount()) {
60870             return;
60871         }
60872         // reset number of rows in cells.
60873         Roo.each(this.cells.elements, function(c){
60874             c.rows = 0;
60875         });
60876         
60877         this.eventStore.each(function(e) {
60878             this.clearEvent(e);
60879         },this);
60880         
60881     },
60882     
60883     clearEvent : function(ev)
60884     {
60885         if (ev.els) {
60886             Roo.each(ev.els, function(el) {
60887                 el.un('mouseenter' ,this.onEventEnter, this);
60888                 el.un('mouseleave' ,this.onEventLeave, this);
60889                 el.remove();
60890             },this);
60891             ev.els = [];
60892         }
60893     },
60894     
60895     
60896     renderEvent : function(ev,ctr) {
60897         if (!ctr) {
60898              ctr = this.view.el.select('.fc-event-container',true).first();
60899         }
60900         
60901          
60902         this.clearEvent(ev);
60903             //code
60904        
60905         
60906         
60907         ev.els = [];
60908         var cells = ev.cells;
60909         var rows = ev.rows;
60910         this.fireEvent('eventrender', this, ev);
60911         
60912         for(var i =0; i < rows.length; i++) {
60913             
60914             cls = '';
60915             if (i == 0) {
60916                 cls += ' fc-event-start';
60917             }
60918             if ((i+1) == rows.length) {
60919                 cls += ' fc-event-end';
60920             }
60921             
60922             //Roo.log(ev.data);
60923             // how many rows should it span..
60924             var cg = this.eventTmpl.append(ctr,Roo.apply({
60925                 fccls : cls
60926                 
60927             }, ev.data) , true);
60928             
60929             
60930             cg.on('mouseenter' ,this.onEventEnter, this, ev);
60931             cg.on('mouseleave' ,this.onEventLeave, this, ev);
60932             cg.on('click', this.onEventClick, this, ev);
60933             
60934             ev.els.push(cg);
60935             
60936             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
60937             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
60938             //Roo.log(cg);
60939              
60940             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
60941             cg.setWidth(ebox.right - sbox.x -2);
60942         }
60943     },
60944     
60945     renderEvents: function()
60946     {   
60947         // first make sure there is enough space..
60948         
60949         if (!this.eventTmpl) {
60950             this.eventTmpl = new Roo.Template(
60951                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
60952                     '<div class="fc-event-inner">' +
60953                         '<span class="fc-event-time">{time}</span>' +
60954                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
60955                     '</div>' +
60956                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
60957                 '</div>'
60958             );
60959                 
60960         }
60961                
60962         
60963         
60964         this.cells.each(function(c) {
60965             //Roo.log(c.select('.fc-day-content div',true).first());
60966             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
60967         });
60968         
60969         var ctr = this.view.el.select('.fc-event-container',true).first();
60970         
60971         var cls;
60972         this.eventStore.each(function(ev){
60973             
60974             this.renderEvent(ev);
60975              
60976              
60977         }, this);
60978         this.view.layout();
60979         
60980     },
60981     
60982     onEventEnter: function (e, el,event,d) {
60983         this.fireEvent('evententer', this, el, event);
60984     },
60985     
60986     onEventLeave: function (e, el,event,d) {
60987         this.fireEvent('eventleave', this, el, event);
60988     },
60989     
60990     onEventClick: function (e, el,event,d) {
60991         this.fireEvent('eventclick', this, el, event);
60992     },
60993     
60994     onMonthChange: function () {
60995         this.store.load();
60996     },
60997     
60998     onLoad: function () {
60999         
61000         //Roo.log('calendar onload');
61001 //         
61002         if(this.eventStore.getCount() > 0){
61003             
61004            
61005             
61006             this.eventStore.each(function(d){
61007                 
61008                 
61009                 // FIXME..
61010                 var add =   d.data;
61011                 if (typeof(add.end_dt) == 'undefined')  {
61012                     Roo.log("Missing End time in calendar data: ");
61013                     Roo.log(d);
61014                     return;
61015                 }
61016                 if (typeof(add.start_dt) == 'undefined')  {
61017                     Roo.log("Missing Start time in calendar data: ");
61018                     Roo.log(d);
61019                     return;
61020                 }
61021                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61022                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61023                 add.id = add.id || d.id;
61024                 add.title = add.title || '??';
61025                 
61026                 this.addItem(d);
61027                 
61028              
61029             },this);
61030         }
61031         
61032         this.renderEvents();
61033     }
61034     
61035
61036 });
61037 /*
61038  grid : {
61039                 xtype: 'Grid',
61040                 xns: Roo.grid,
61041                 listeners : {
61042                     render : function ()
61043                     {
61044                         _this.grid = this;
61045                         
61046                         if (!this.view.el.hasClass('course-timesheet')) {
61047                             this.view.el.addClass('course-timesheet');
61048                         }
61049                         if (this.tsStyle) {
61050                             this.ds.load({});
61051                             return; 
61052                         }
61053                         Roo.log('width');
61054                         Roo.log(_this.grid.view.el.getWidth());
61055                         
61056                         
61057                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61058                             '.course-timesheet .x-grid-row' : {
61059                                 height: '80px'
61060                             },
61061                             '.x-grid-row td' : {
61062                                 'vertical-align' : 0
61063                             },
61064                             '.course-edit-link' : {
61065                                 'color' : 'blue',
61066                                 'text-overflow' : 'ellipsis',
61067                                 'overflow' : 'hidden',
61068                                 'white-space' : 'nowrap',
61069                                 'cursor' : 'pointer'
61070                             },
61071                             '.sub-link' : {
61072                                 'color' : 'green'
61073                             },
61074                             '.de-act-sup-link' : {
61075                                 'color' : 'purple',
61076                                 'text-decoration' : 'line-through'
61077                             },
61078                             '.de-act-link' : {
61079                                 'color' : 'red',
61080                                 'text-decoration' : 'line-through'
61081                             },
61082                             '.course-timesheet .course-highlight' : {
61083                                 'border-top-style': 'dashed !important',
61084                                 'border-bottom-bottom': 'dashed !important'
61085                             },
61086                             '.course-timesheet .course-item' : {
61087                                 'font-family'   : 'tahoma, arial, helvetica',
61088                                 'font-size'     : '11px',
61089                                 'overflow'      : 'hidden',
61090                                 'padding-left'  : '10px',
61091                                 'padding-right' : '10px',
61092                                 'padding-top' : '10px' 
61093                             }
61094                             
61095                         }, Roo.id());
61096                                 this.ds.load({});
61097                     }
61098                 },
61099                 autoWidth : true,
61100                 monitorWindowResize : false,
61101                 cellrenderer : function(v,x,r)
61102                 {
61103                     return v;
61104                 },
61105                 sm : {
61106                     xtype: 'CellSelectionModel',
61107                     xns: Roo.grid
61108                 },
61109                 dataSource : {
61110                     xtype: 'Store',
61111                     xns: Roo.data,
61112                     listeners : {
61113                         beforeload : function (_self, options)
61114                         {
61115                             options.params = options.params || {};
61116                             options.params._month = _this.monthField.getValue();
61117                             options.params.limit = 9999;
61118                             options.params['sort'] = 'when_dt';    
61119                             options.params['dir'] = 'ASC';    
61120                             this.proxy.loadResponse = this.loadResponse;
61121                             Roo.log("load?");
61122                             //this.addColumns();
61123                         },
61124                         load : function (_self, records, options)
61125                         {
61126                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61127                                 // if you click on the translation.. you can edit it...
61128                                 var el = Roo.get(this);
61129                                 var id = el.dom.getAttribute('data-id');
61130                                 var d = el.dom.getAttribute('data-date');
61131                                 var t = el.dom.getAttribute('data-time');
61132                                 //var id = this.child('span').dom.textContent;
61133                                 
61134                                 //Roo.log(this);
61135                                 Pman.Dialog.CourseCalendar.show({
61136                                     id : id,
61137                                     when_d : d,
61138                                     when_t : t,
61139                                     productitem_active : id ? 1 : 0
61140                                 }, function() {
61141                                     _this.grid.ds.load({});
61142                                 });
61143                            
61144                            });
61145                            
61146                            _this.panel.fireEvent('resize', [ '', '' ]);
61147                         }
61148                     },
61149                     loadResponse : function(o, success, response){
61150                             // this is overridden on before load..
61151                             
61152                             Roo.log("our code?");       
61153                             //Roo.log(success);
61154                             //Roo.log(response)
61155                             delete this.activeRequest;
61156                             if(!success){
61157                                 this.fireEvent("loadexception", this, o, response);
61158                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61159                                 return;
61160                             }
61161                             var result;
61162                             try {
61163                                 result = o.reader.read(response);
61164                             }catch(e){
61165                                 Roo.log("load exception?");
61166                                 this.fireEvent("loadexception", this, o, response, e);
61167                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61168                                 return;
61169                             }
61170                             Roo.log("ready...");        
61171                             // loop through result.records;
61172                             // and set this.tdate[date] = [] << array of records..
61173                             _this.tdata  = {};
61174                             Roo.each(result.records, function(r){
61175                                 //Roo.log(r.data);
61176                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61177                                     _this.tdata[r.data.when_dt.format('j')] = [];
61178                                 }
61179                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61180                             });
61181                             
61182                             //Roo.log(_this.tdata);
61183                             
61184                             result.records = [];
61185                             result.totalRecords = 6;
61186                     
61187                             // let's generate some duumy records for the rows.
61188                             //var st = _this.dateField.getValue();
61189                             
61190                             // work out monday..
61191                             //st = st.add(Date.DAY, -1 * st.format('w'));
61192                             
61193                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61194                             
61195                             var firstOfMonth = date.getFirstDayOfMonth();
61196                             var days = date.getDaysInMonth();
61197                             var d = 1;
61198                             var firstAdded = false;
61199                             for (var i = 0; i < result.totalRecords ; i++) {
61200                                 //var d= st.add(Date.DAY, i);
61201                                 var row = {};
61202                                 var added = 0;
61203                                 for(var w = 0 ; w < 7 ; w++){
61204                                     if(!firstAdded && firstOfMonth != w){
61205                                         continue;
61206                                     }
61207                                     if(d > days){
61208                                         continue;
61209                                     }
61210                                     firstAdded = true;
61211                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61212                                     row['weekday'+w] = String.format(
61213                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61214                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61215                                                     d,
61216                                                     date.format('Y-m-')+dd
61217                                                 );
61218                                     added++;
61219                                     if(typeof(_this.tdata[d]) != 'undefined'){
61220                                         Roo.each(_this.tdata[d], function(r){
61221                                             var is_sub = '';
61222                                             var deactive = '';
61223                                             var id = r.id;
61224                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61225                                             if(r.parent_id*1>0){
61226                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61227                                                 id = r.parent_id;
61228                                             }
61229                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61230                                                 deactive = 'de-act-link';
61231                                             }
61232                                             
61233                                             row['weekday'+w] += String.format(
61234                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61235                                                     id, //0
61236                                                     r.product_id_name, //1
61237                                                     r.when_dt.format('h:ia'), //2
61238                                                     is_sub, //3
61239                                                     deactive, //4
61240                                                     desc // 5
61241                                             );
61242                                         });
61243                                     }
61244                                     d++;
61245                                 }
61246                                 
61247                                 // only do this if something added..
61248                                 if(added > 0){ 
61249                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61250                                 }
61251                                 
61252                                 
61253                                 // push it twice. (second one with an hour..
61254                                 
61255                             }
61256                             //Roo.log(result);
61257                             this.fireEvent("load", this, o, o.request.arg);
61258                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61259                         },
61260                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61261                     proxy : {
61262                         xtype: 'HttpProxy',
61263                         xns: Roo.data,
61264                         method : 'GET',
61265                         url : baseURL + '/Roo/Shop_course.php'
61266                     },
61267                     reader : {
61268                         xtype: 'JsonReader',
61269                         xns: Roo.data,
61270                         id : 'id',
61271                         fields : [
61272                             {
61273                                 'name': 'id',
61274                                 'type': 'int'
61275                             },
61276                             {
61277                                 'name': 'when_dt',
61278                                 'type': 'string'
61279                             },
61280                             {
61281                                 'name': 'end_dt',
61282                                 'type': 'string'
61283                             },
61284                             {
61285                                 'name': 'parent_id',
61286                                 'type': 'int'
61287                             },
61288                             {
61289                                 'name': 'product_id',
61290                                 'type': 'int'
61291                             },
61292                             {
61293                                 'name': 'productitem_id',
61294                                 'type': 'int'
61295                             },
61296                             {
61297                                 'name': 'guid',
61298                                 'type': 'int'
61299                             }
61300                         ]
61301                     }
61302                 },
61303                 toolbar : {
61304                     xtype: 'Toolbar',
61305                     xns: Roo,
61306                     items : [
61307                         {
61308                             xtype: 'Button',
61309                             xns: Roo.Toolbar,
61310                             listeners : {
61311                                 click : function (_self, e)
61312                                 {
61313                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61314                                     sd.setMonth(sd.getMonth()-1);
61315                                     _this.monthField.setValue(sd.format('Y-m-d'));
61316                                     _this.grid.ds.load({});
61317                                 }
61318                             },
61319                             text : "Back"
61320                         },
61321                         {
61322                             xtype: 'Separator',
61323                             xns: Roo.Toolbar
61324                         },
61325                         {
61326                             xtype: 'MonthField',
61327                             xns: Roo.form,
61328                             listeners : {
61329                                 render : function (_self)
61330                                 {
61331                                     _this.monthField = _self;
61332                                    // _this.monthField.set  today
61333                                 },
61334                                 select : function (combo, date)
61335                                 {
61336                                     _this.grid.ds.load({});
61337                                 }
61338                             },
61339                             value : (function() { return new Date(); })()
61340                         },
61341                         {
61342                             xtype: 'Separator',
61343                             xns: Roo.Toolbar
61344                         },
61345                         {
61346                             xtype: 'TextItem',
61347                             xns: Roo.Toolbar,
61348                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61349                         },
61350                         {
61351                             xtype: 'Fill',
61352                             xns: Roo.Toolbar
61353                         },
61354                         {
61355                             xtype: 'Button',
61356                             xns: Roo.Toolbar,
61357                             listeners : {
61358                                 click : function (_self, e)
61359                                 {
61360                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61361                                     sd.setMonth(sd.getMonth()+1);
61362                                     _this.monthField.setValue(sd.format('Y-m-d'));
61363                                     _this.grid.ds.load({});
61364                                 }
61365                             },
61366                             text : "Next"
61367                         }
61368                     ]
61369                 },
61370                  
61371             }
61372         };
61373         
61374         *//*
61375  * Based on:
61376  * Ext JS Library 1.1.1
61377  * Copyright(c) 2006-2007, Ext JS, LLC.
61378  *
61379  * Originally Released Under LGPL - original licence link has changed is not relivant.
61380  *
61381  * Fork - LGPL
61382  * <script type="text/javascript">
61383  */
61384  
61385 /**
61386  * @class Roo.LoadMask
61387  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61388  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61389  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61390  * element's UpdateManager load indicator and will be destroyed after the initial load.
61391  * @constructor
61392  * Create a new LoadMask
61393  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61394  * @param {Object} config The config object
61395  */
61396 Roo.LoadMask = function(el, config){
61397     this.el = Roo.get(el);
61398     Roo.apply(this, config);
61399     if(this.store){
61400         this.store.on('beforeload', this.onBeforeLoad, this);
61401         this.store.on('load', this.onLoad, this);
61402         this.store.on('loadexception', this.onLoadException, this);
61403         this.removeMask = false;
61404     }else{
61405         var um = this.el.getUpdateManager();
61406         um.showLoadIndicator = false; // disable the default indicator
61407         um.on('beforeupdate', this.onBeforeLoad, this);
61408         um.on('update', this.onLoad, this);
61409         um.on('failure', this.onLoad, this);
61410         this.removeMask = true;
61411     }
61412 };
61413
61414 Roo.LoadMask.prototype = {
61415     /**
61416      * @cfg {Boolean} removeMask
61417      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61418      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61419      */
61420     /**
61421      * @cfg {String} msg
61422      * The text to display in a centered loading message box (defaults to 'Loading...')
61423      */
61424     msg : 'Loading...',
61425     /**
61426      * @cfg {String} msgCls
61427      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61428      */
61429     msgCls : 'x-mask-loading',
61430
61431     /**
61432      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61433      * @type Boolean
61434      */
61435     disabled: false,
61436
61437     /**
61438      * Disables the mask to prevent it from being displayed
61439      */
61440     disable : function(){
61441        this.disabled = true;
61442     },
61443
61444     /**
61445      * Enables the mask so that it can be displayed
61446      */
61447     enable : function(){
61448         this.disabled = false;
61449     },
61450     
61451     onLoadException : function()
61452     {
61453         Roo.log(arguments);
61454         
61455         if (typeof(arguments[3]) != 'undefined') {
61456             Roo.MessageBox.alert("Error loading",arguments[3]);
61457         } 
61458         /*
61459         try {
61460             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61461                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61462             }   
61463         } catch(e) {
61464             
61465         }
61466         */
61467     
61468         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61469     },
61470     // private
61471     onLoad : function()
61472     {
61473         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61474     },
61475
61476     // private
61477     onBeforeLoad : function(){
61478         if(!this.disabled){
61479             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61480         }
61481     },
61482
61483     // private
61484     destroy : function(){
61485         if(this.store){
61486             this.store.un('beforeload', this.onBeforeLoad, this);
61487             this.store.un('load', this.onLoad, this);
61488             this.store.un('loadexception', this.onLoadException, this);
61489         }else{
61490             var um = this.el.getUpdateManager();
61491             um.un('beforeupdate', this.onBeforeLoad, this);
61492             um.un('update', this.onLoad, this);
61493             um.un('failure', this.onLoad, this);
61494         }
61495     }
61496 };/*
61497  * Based on:
61498  * Ext JS Library 1.1.1
61499  * Copyright(c) 2006-2007, Ext JS, LLC.
61500  *
61501  * Originally Released Under LGPL - original licence link has changed is not relivant.
61502  *
61503  * Fork - LGPL
61504  * <script type="text/javascript">
61505  */
61506
61507
61508 /**
61509  * @class Roo.XTemplate
61510  * @extends Roo.Template
61511  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61512 <pre><code>
61513 var t = new Roo.XTemplate(
61514         '&lt;select name="{name}"&gt;',
61515                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61516         '&lt;/select&gt;'
61517 );
61518  
61519 // then append, applying the master template values
61520  </code></pre>
61521  *
61522  * Supported features:
61523  *
61524  *  Tags:
61525
61526 <pre><code>
61527       {a_variable} - output encoded.
61528       {a_variable.format:("Y-m-d")} - call a method on the variable
61529       {a_variable:raw} - unencoded output
61530       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61531       {a_variable:this.method_on_template(...)} - call a method on the template object.
61532  
61533 </code></pre>
61534  *  The tpl tag:
61535 <pre><code>
61536         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61537         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61538         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61539         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61540   
61541         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61542         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61543 </code></pre>
61544  *      
61545  */
61546 Roo.XTemplate = function()
61547 {
61548     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61549     if (this.html) {
61550         this.compile();
61551     }
61552 };
61553
61554
61555 Roo.extend(Roo.XTemplate, Roo.Template, {
61556
61557     /**
61558      * The various sub templates
61559      */
61560     tpls : false,
61561     /**
61562      *
61563      * basic tag replacing syntax
61564      * WORD:WORD()
61565      *
61566      * // you can fake an object call by doing this
61567      *  x.t:(test,tesT) 
61568      * 
61569      */
61570     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61571
61572     /**
61573      * compile the template
61574      *
61575      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61576      *
61577      */
61578     compile: function()
61579     {
61580         var s = this.html;
61581      
61582         s = ['<tpl>', s, '</tpl>'].join('');
61583     
61584         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61585             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61586             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61587             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61588             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61589             m,
61590             id     = 0,
61591             tpls   = [];
61592     
61593         while(true == !!(m = s.match(re))){
61594             var forMatch   = m[0].match(nameRe),
61595                 ifMatch   = m[0].match(ifRe),
61596                 execMatch   = m[0].match(execRe),
61597                 namedMatch   = m[0].match(namedRe),
61598                 
61599                 exp  = null, 
61600                 fn   = null,
61601                 exec = null,
61602                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61603                 
61604             if (ifMatch) {
61605                 // if - puts fn into test..
61606                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61607                 if(exp){
61608                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61609                 }
61610             }
61611             
61612             if (execMatch) {
61613                 // exec - calls a function... returns empty if true is  returned.
61614                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61615                 if(exp){
61616                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61617                 }
61618             }
61619             
61620             
61621             if (name) {
61622                 // for = 
61623                 switch(name){
61624                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61625                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61626                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61627                 }
61628             }
61629             var uid = namedMatch ? namedMatch[1] : id;
61630             
61631             
61632             tpls.push({
61633                 id:     namedMatch ? namedMatch[1] : id,
61634                 target: name,
61635                 exec:   exec,
61636                 test:   fn,
61637                 body:   m[1] || ''
61638             });
61639             if (namedMatch) {
61640                 s = s.replace(m[0], '');
61641             } else { 
61642                 s = s.replace(m[0], '{xtpl'+ id + '}');
61643             }
61644             ++id;
61645         }
61646         this.tpls = [];
61647         for(var i = tpls.length-1; i >= 0; --i){
61648             this.compileTpl(tpls[i]);
61649             this.tpls[tpls[i].id] = tpls[i];
61650         }
61651         this.master = tpls[tpls.length-1];
61652         return this;
61653     },
61654     /**
61655      * same as applyTemplate, except it's done to one of the subTemplates
61656      * when using named templates, you can do:
61657      *
61658      * var str = pl.applySubTemplate('your-name', values);
61659      *
61660      * 
61661      * @param {Number} id of the template
61662      * @param {Object} values to apply to template
61663      * @param {Object} parent (normaly the instance of this object)
61664      */
61665     applySubTemplate : function(id, values, parent)
61666     {
61667         
61668         
61669         var t = this.tpls[id];
61670         
61671         
61672         try { 
61673             if(t.test && !t.test.call(this, values, parent)){
61674                 return '';
61675             }
61676         } catch(e) {
61677             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61678             Roo.log(e.toString());
61679             Roo.log(t.test);
61680             return ''
61681         }
61682         try { 
61683             
61684             if(t.exec && t.exec.call(this, values, parent)){
61685                 return '';
61686             }
61687         } catch(e) {
61688             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61689             Roo.log(e.toString());
61690             Roo.log(t.exec);
61691             return ''
61692         }
61693         try {
61694             var vs = t.target ? t.target.call(this, values, parent) : values;
61695             parent = t.target ? values : parent;
61696             if(t.target && vs instanceof Array){
61697                 var buf = [];
61698                 for(var i = 0, len = vs.length; i < len; i++){
61699                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61700                 }
61701                 return buf.join('');
61702             }
61703             return t.compiled.call(this, vs, parent);
61704         } catch (e) {
61705             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61706             Roo.log(e.toString());
61707             Roo.log(t.compiled);
61708             return '';
61709         }
61710     },
61711
61712     compileTpl : function(tpl)
61713     {
61714         var fm = Roo.util.Format;
61715         var useF = this.disableFormats !== true;
61716         var sep = Roo.isGecko ? "+" : ",";
61717         var undef = function(str) {
61718             Roo.log("Property not found :"  + str);
61719             return '';
61720         };
61721         
61722         var fn = function(m, name, format, args)
61723         {
61724             //Roo.log(arguments);
61725             args = args ? args.replace(/\\'/g,"'") : args;
61726             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61727             if (typeof(format) == 'undefined') {
61728                 format= 'htmlEncode';
61729             }
61730             if (format == 'raw' ) {
61731                 format = false;
61732             }
61733             
61734             if(name.substr(0, 4) == 'xtpl'){
61735                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61736             }
61737             
61738             // build an array of options to determine if value is undefined..
61739             
61740             // basically get 'xxxx.yyyy' then do
61741             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61742             //    (function () { Roo.log("Property not found"); return ''; })() :
61743             //    ......
61744             
61745             var udef_ar = [];
61746             var lookfor = '';
61747             Roo.each(name.split('.'), function(st) {
61748                 lookfor += (lookfor.length ? '.': '') + st;
61749                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61750             });
61751             
61752             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61753             
61754             
61755             if(format && useF){
61756                 
61757                 args = args ? ',' + args : "";
61758                  
61759                 if(format.substr(0, 5) != "this."){
61760                     format = "fm." + format + '(';
61761                 }else{
61762                     format = 'this.call("'+ format.substr(5) + '", ';
61763                     args = ", values";
61764                 }
61765                 
61766                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61767             }
61768              
61769             if (args.length) {
61770                 // called with xxyx.yuu:(test,test)
61771                 // change to ()
61772                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61773             }
61774             // raw.. - :raw modifier..
61775             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61776             
61777         };
61778         var body;
61779         // branched to use + in gecko and [].join() in others
61780         if(Roo.isGecko){
61781             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61782                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61783                     "';};};";
61784         }else{
61785             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61786             body.push(tpl.body.replace(/(\r\n|\n)/g,
61787                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61788             body.push("'].join('');};};");
61789             body = body.join('');
61790         }
61791         
61792         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61793        
61794         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61795         eval(body);
61796         
61797         return this;
61798     },
61799
61800     applyTemplate : function(values){
61801         return this.master.compiled.call(this, values, {});
61802         //var s = this.subs;
61803     },
61804
61805     apply : function(){
61806         return this.applyTemplate.apply(this, arguments);
61807     }
61808
61809  });
61810
61811 Roo.XTemplate.from = function(el){
61812     el = Roo.getDom(el);
61813     return new Roo.XTemplate(el.value || el.innerHTML);
61814 };